blob: 619010b27241a72b0cf8d65d112bc37123f58c12 [file] [log] [blame]
ager@chromium.org71daaf62009-04-01 07:22:49 +00001// Copyright 2009 the V8 project authors. All rights reserved.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +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
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000030#include "codegen-inl.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000031#include "register-allocator-inl.h"
32#include "scopes.h"
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000033
34namespace v8 { namespace internal {
35
ager@chromium.org65dad4b2009-04-23 08:48:43 +000036#define __ ACCESS_MASM(masm_)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000037
38// -------------------------------------------------------------------------
39// VirtualFrame implementation.
40
41// On entry to a function, the virtual frame already contains the receiver,
42// the parameters, and a return address. All frame elements are in memory.
43VirtualFrame::VirtualFrame(CodeGenerator* cgen)
44 : cgen_(cgen),
45 masm_(cgen->masm()),
ager@chromium.org71daaf62009-04-01 07:22:49 +000046 elements_(cgen->scope()->num_parameters()
47 + cgen->scope()->num_stack_slots()
48 + kPreallocatedElements),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000049 parameter_count_(cgen->scope()->num_parameters()),
50 local_count_(0),
51 stack_pointer_(parameter_count_ + 1), // 0-based index of TOS.
52 frame_pointer_(kIllegalIndex) {
53 for (int i = 0; i < parameter_count_ + 2; i++) {
54 elements_.Add(FrameElement::MemoryElement());
55 }
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +000056 for (int i = 0; i < kNumRegisters; i++) {
57 register_locations_[i] = kIllegalIndex;
58 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000059}
60
61
ager@chromium.org41826e72009-03-30 13:30:57 +000062void VirtualFrame::SyncElementBelowStackPointer(int index) {
63 // Emit code to write elements below the stack pointer to their
64 // (already allocated) stack address.
65 ASSERT(index <= stack_pointer_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000066 FrameElement element = elements_[index];
ager@chromium.org41826e72009-03-30 13:30:57 +000067 ASSERT(!element.is_synced());
68 switch (element.type()) {
69 case FrameElement::INVALID:
70 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000071
ager@chromium.org41826e72009-03-30 13:30:57 +000072 case FrameElement::MEMORY:
73 // This function should not be called with synced elements.
74 // (memory elements are always synced).
75 UNREACHABLE();
76 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000077
ager@chromium.org41826e72009-03-30 13:30:57 +000078 case FrameElement::REGISTER:
79 __ mov(Operand(ebp, fp_relative(index)), element.reg());
80 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000081
ager@chromium.org41826e72009-03-30 13:30:57 +000082 case FrameElement::CONSTANT:
83 if (cgen_->IsUnsafeSmi(element.handle())) {
84 Result temp = cgen_->allocator()->Allocate();
85 ASSERT(temp.is_valid());
86 cgen_->LoadUnsafeSmi(temp.reg(), element.handle());
87 __ mov(Operand(ebp, fp_relative(index)), temp.reg());
88 } else {
89 __ Set(Operand(ebp, fp_relative(index)),
90 Immediate(element.handle()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000091 }
ager@chromium.org41826e72009-03-30 13:30:57 +000092 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000093
ager@chromium.org41826e72009-03-30 13:30:57 +000094 case FrameElement::COPY: {
95 int backing_index = element.index();
96 FrameElement backing_element = elements_[backing_index];
97 if (backing_element.is_memory()) {
98 Result temp = cgen_->allocator()->Allocate();
99 ASSERT(temp.is_valid());
100 __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index)));
101 __ mov(Operand(ebp, fp_relative(index)), temp.reg());
102 } else {
103 ASSERT(backing_element.is_register());
104 __ mov(Operand(ebp, fp_relative(index)), backing_element.reg());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000105 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000106 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000107 }
108 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000109 elements_[index].set_sync();
110}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000111
ager@chromium.org41826e72009-03-30 13:30:57 +0000112
113void VirtualFrame::SyncElementByPushing(int index) {
114 // Sync an element of the frame that is just above the stack pointer
115 // by pushing it.
116 ASSERT(index == stack_pointer_ + 1);
117 stack_pointer_++;
118 FrameElement element = elements_[index];
119
120 switch (element.type()) {
121 case FrameElement::INVALID:
122 __ push(Immediate(Smi::FromInt(0)));
123 break;
124
125 case FrameElement::MEMORY:
126 // No memory elements exist above the stack pointer.
127 UNREACHABLE();
128 break;
129
130 case FrameElement::REGISTER:
131 __ push(element.reg());
132 break;
133
134 case FrameElement::CONSTANT:
135 if (cgen_->IsUnsafeSmi(element.handle())) {
136 Result temp = cgen_->allocator()->Allocate();
137 ASSERT(temp.is_valid());
138 cgen_->LoadUnsafeSmi(temp.reg(), element.handle());
139 __ push(temp.reg());
140 } else {
141 __ push(Immediate(element.handle()));
142 }
143 break;
144
145 case FrameElement::COPY: {
146 int backing_index = element.index();
147 FrameElement backing = elements_[backing_index];
148 ASSERT(backing.is_memory() || backing.is_register());
149 if (backing.is_memory()) {
150 __ push(Operand(ebp, fp_relative(backing_index)));
151 } else {
152 __ push(backing.reg());
153 }
154 break;
155 }
156 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000157 elements_[index].set_sync();
158}
159
160
ager@chromium.org3a37e9b2009-04-27 09:26:21 +0000161// Clear the dirty bits for the range of elements in
162// [min(stack_pointer_ + 1,begin), end].
163void VirtualFrame::SyncRange(int begin, int end) {
164 ASSERT(begin >= 0);
165 ASSERT(end < elements_.length());
166 // Sync elements below the range if they have not been materialized
167 // on the stack.
168 int start = Min(begin, stack_pointer_ + 1);
169
170 // If positive we have to adjust the stack pointer.
171 int delta = end - stack_pointer_;
172 if (delta > 0) {
173 stack_pointer_ = end;
174 __ sub(Operand(esp), Immediate(delta * kPointerSize));
175 }
176
177 for (int i = start; i <= end; i++) {
178 if (!elements_[i].is_synced()) SyncElementBelowStackPointer(i);
179 }
180}
181
182
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000183void VirtualFrame::MergeTo(VirtualFrame* expected) {
184 Comment cmnt(masm_, "[ Merge frame");
185 // We should always be merging the code generator's current frame to an
186 // expected frame.
187 ASSERT(cgen_->frame() == this);
188
189 // Adjust the stack pointer upward (toward the top of the virtual
190 // frame) if necessary.
191 if (stack_pointer_ < expected->stack_pointer_) {
192 int difference = expected->stack_pointer_ - stack_pointer_;
193 stack_pointer_ = expected->stack_pointer_;
194 __ sub(Operand(esp), Immediate(difference * kPointerSize));
195 }
196
197 MergeMoveRegistersToMemory(expected);
198 MergeMoveRegistersToRegisters(expected);
199 MergeMoveMemoryToRegisters(expected);
200
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000201 // Fix any sync flag problems from the bottom-up and make the copied
202 // flags exact. This assumes that the backing store of copies is
203 // always lower in the frame.
204 for (int i = 0; i < elements_.length(); i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000205 FrameElement source = elements_[i];
206 FrameElement target = expected->elements_[i];
207 if (source.is_synced() && !target.is_synced()) {
208 elements_[i].clear_sync();
209 } else if (!source.is_synced() && target.is_synced()) {
210 SyncElementAt(i);
211 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000212 elements_[i].clear_copied();
213 if (elements_[i].is_copy()) {
214 elements_[elements_[i].index()].set_copied();
215 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000216 }
217
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000218 // Adjust the stack pointer downward if necessary.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000219 if (stack_pointer_ > expected->stack_pointer_) {
220 int difference = stack_pointer_ - expected->stack_pointer_;
221 stack_pointer_ = expected->stack_pointer_;
222 __ add(Operand(esp), Immediate(difference * kPointerSize));
223 }
224
225 // At this point, the frames should be identical.
226 ASSERT(Equals(expected));
227}
228
229
230void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
231 ASSERT(stack_pointer_ >= expected->stack_pointer_);
232
233 // Move registers, constants, and copies to memory. Perform moves
234 // from the top downward in the frame in order to leave the backing
235 // stores of copies in registers.
236 //
237 // Moving memory-backed copies to memory requires a spare register
238 // for the memory-to-memory moves. Since we are performing a merge,
239 // we use esi (which is already saved in the frame). We keep track
240 // of the index of the frame element esi is caching or kIllegalIndex
241 // if esi has not been disturbed.
242 int esi_caches = kIllegalIndex;
243 // A "singleton" memory element.
244 FrameElement memory_element = FrameElement::MemoryElement();
245 // Loop downward from the stack pointer or the top of the frame if
246 // the stack pointer is floating above the frame.
247 int start = Min(stack_pointer_, elements_.length() - 1);
248 for (int i = start; i >= 0; i--) {
249 FrameElement target = expected->elements_[i];
250 if (target.is_memory()) {
251 FrameElement source = elements_[i];
252 switch (source.type()) {
253 case FrameElement::INVALID:
254 // Not a legal merge move.
255 UNREACHABLE();
256 break;
257
258 case FrameElement::MEMORY:
259 // Already in place.
260 break;
261
262 case FrameElement::REGISTER:
263 Unuse(source.reg());
264 if (!source.is_synced()) {
265 __ mov(Operand(ebp, fp_relative(i)), source.reg());
266 }
267 break;
268
269 case FrameElement::CONSTANT:
270 if (!source.is_synced()) {
271 if (cgen_->IsUnsafeSmi(source.handle())) {
272 esi_caches = i;
273 cgen_->LoadUnsafeSmi(esi, source.handle());
274 __ mov(Operand(ebp, fp_relative(i)), esi);
275 } else {
276 __ Set(Operand(ebp, fp_relative(i)), Immediate(source.handle()));
277 }
278 }
279 break;
280
281 case FrameElement::COPY:
282 if (!source.is_synced()) {
283 int backing_index = source.index();
284 FrameElement backing_element = elements_[backing_index];
285 if (backing_element.is_memory()) {
286 // If we have to spill a register, we spill esi.
287 if (esi_caches != backing_index) {
288 esi_caches = backing_index;
289 __ mov(esi, Operand(ebp, fp_relative(backing_index)));
290 }
291 __ mov(Operand(ebp, fp_relative(i)), esi);
292 } else {
293 ASSERT(backing_element.is_register());
294 __ mov(Operand(ebp, fp_relative(i)), backing_element.reg());
295 }
296 }
297 break;
298 }
299 elements_[i] = memory_element;
300 }
301 }
302
303 if (esi_caches != kIllegalIndex) {
304 __ mov(esi, Operand(ebp, fp_relative(context_index())));
305 }
306}
307
308
309void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) {
310 // We have already done X-to-memory moves.
311 ASSERT(stack_pointer_ >= expected->stack_pointer_);
312
ager@chromium.org5ec48922009-05-05 07:25:34 +0000313 for (int i = 0; i < kNumRegisters; i++) {
314 // Move the right value into register i if it is currently in a register.
315 int index = expected->register_locations_[i];
316 int use_index = register_locations_[i];
317 // Fast check if register is unused in target or already correct
318 if (index != kIllegalIndex
319 && index != use_index
320 && elements_[index].is_register()) {
321 Register source = elements_[index].reg();
322 Register target = { i };
323 if (use_index == kIllegalIndex) { // Target is currently unused.
324 // Copy contents of source from source to target.
325 // Set frame element register to target.
326 elements_[index].set_reg(target);
327 Use(target, index);
328 Unuse(source);
329 __ mov(target, source);
330 } else {
331 // Exchange contents of registers source and target.
332 elements_[use_index].set_reg(source);
333 elements_[index].set_reg(target);
334 register_locations_[target.code()] = index;
335 register_locations_[source.code()] = use_index;
336 __ xchg(source, target);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000337 }
338 }
ager@chromium.org5ec48922009-05-05 07:25:34 +0000339 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000340}
341
342
343void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) {
344 // Move memory, constants, and copies to registers. This is the
345 // final step and is done from the bottom up so that the backing
346 // elements of copies are in their correct locations when we
347 // encounter the copies.
ager@chromium.org5ec48922009-05-05 07:25:34 +0000348 for (int i = 0; i < kNumRegisters; i++) {
349 int index = expected->register_locations_[i];
350 if (index != kIllegalIndex) {
351 FrameElement source = elements_[index];
352 FrameElement target = expected->elements_[index];
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000353 switch (source.type()) {
354 case FrameElement::INVALID: // Fall through.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000355 UNREACHABLE();
356 break;
ager@chromium.org5ec48922009-05-05 07:25:34 +0000357 case FrameElement::REGISTER:
358 ASSERT(source.reg().is(target.reg()));
359 continue; // Go to next iteration. Skips Use(target.reg()) below.
360 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000361 case FrameElement::MEMORY:
ager@chromium.org5ec48922009-05-05 07:25:34 +0000362 ASSERT(index <= stack_pointer_);
363 __ mov(target.reg(), Operand(ebp, fp_relative(index)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000364 break;
365
366 case FrameElement::CONSTANT:
367 if (cgen_->IsUnsafeSmi(source.handle())) {
368 cgen_->LoadUnsafeSmi(target.reg(), source.handle());
369 } else {
370 __ Set(target.reg(), Immediate(source.handle()));
371 }
372 break;
373
374 case FrameElement::COPY: {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000375 int backing_index = source.index();
376 FrameElement backing = elements_[backing_index];
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000377 ASSERT(backing.is_memory() || backing.is_register());
378 if (backing.is_memory()) {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000379 ASSERT(backing_index <= stack_pointer_);
380 // Code optimization if backing store should also move
381 // to a register: move backing store to its register first.
382 if (expected->elements_[backing_index].is_register()) {
383 FrameElement new_backing = expected->elements_[backing_index];
384 Register new_backing_reg = new_backing.reg();
385 ASSERT(!is_used(new_backing_reg));
386 elements_[backing_index] = new_backing;
387 Use(new_backing_reg, backing_index);
388 __ mov(new_backing_reg,
389 Operand(ebp, fp_relative(backing_index)));
390 __ mov(target.reg(), new_backing_reg);
391 } else {
392 __ mov(target.reg(), Operand(ebp, fp_relative(backing_index)));
393 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000394 } else {
395 __ mov(target.reg(), backing.reg());
396 }
397 }
398 }
399 // Ensure the proper sync state. If the source was memory no
400 // code needs to be emitted.
ager@chromium.org5ec48922009-05-05 07:25:34 +0000401 if (target.is_synced() && !source.is_synced()) {
402 __ mov(Operand(ebp, fp_relative(index)), target.reg());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000403 }
ager@chromium.org5ec48922009-05-05 07:25:34 +0000404 Use(target.reg(), index);
405 elements_[index] = target;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000406 }
407 }
408}
409
410
411void VirtualFrame::Enter() {
412 // Registers live on entry: esp, ebp, esi, edi.
413 Comment cmnt(masm_, "[ Enter JS frame");
414
415#ifdef DEBUG
416 // Verify that edi contains a JS function. The following code
417 // relies on eax being available for use.
418 __ test(edi, Immediate(kSmiTagMask));
419 __ Check(not_zero,
420 "VirtualFrame::Enter - edi is not a function (smi check).");
421 __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
422 __ Check(equal,
423 "VirtualFrame::Enter - edi is not a function (map check).");
424#endif
425
426 EmitPush(ebp);
427
428 frame_pointer_ = stack_pointer_;
429 __ mov(ebp, Operand(esp));
430
431 // Store the context in the frame. The context is kept in esi and a
432 // copy is stored in the frame. The external reference to esi
433 // remains.
434 EmitPush(esi);
435
436 // Store the function in the frame. The frame owns the register
437 // reference now (ie, it can keep it in edi or spill it later).
438 Push(edi);
439 SyncElementAt(elements_.length() - 1);
440 cgen_->allocator()->Unuse(edi);
441}
442
443
444void VirtualFrame::Exit() {
445 Comment cmnt(masm_, "[ Exit JS frame");
446 // Record the location of the JS exit code for patching when setting
447 // break point.
448 __ RecordJSReturn();
449
450 // Avoid using the leave instruction here, because it is too
451 // short. We need the return sequence to be a least the size of a
452 // call instruction to support patching the exit code in the
453 // debugger. See VisitReturnStatement for the full return sequence.
454 __ mov(esp, Operand(ebp));
455 stack_pointer_ = frame_pointer_;
456 for (int i = elements_.length() - 1; i > stack_pointer_; i--) {
457 FrameElement last = elements_.RemoveLast();
458 if (last.is_register()) {
459 Unuse(last.reg());
460 }
461 }
462
463 frame_pointer_ = kIllegalIndex;
464 EmitPop(ebp);
465}
466
467
468void VirtualFrame::AllocateStackSlots(int count) {
469 ASSERT(height() == 0);
470 local_count_ = count;
471
472 if (count > 0) {
473 Comment cmnt(masm_, "[ Allocate space for locals");
474 // The locals are initialized to a constant (the undefined value), but
475 // we sync them with the actual frame to allocate space for spilling
476 // them later. First sync everything above the stack pointer so we can
477 // use pushes to allocate and initialize the locals.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +0000478 SyncRange(stack_pointer_ + 1, elements_.length() - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000479 Handle<Object> undefined = Factory::undefined_value();
480 FrameElement initial_value =
481 FrameElement::ConstantElement(undefined, FrameElement::SYNCED);
482 Result temp = cgen_->allocator()->Allocate();
483 ASSERT(temp.is_valid());
484 __ Set(temp.reg(), Immediate(undefined));
485 for (int i = 0; i < count; i++) {
486 elements_.Add(initial_value);
487 stack_pointer_++;
488 __ push(temp.reg());
489 }
490 }
491}
492
493
494void VirtualFrame::SaveContextRegister() {
495 ASSERT(elements_[context_index()].is_memory());
496 __ mov(Operand(ebp, fp_relative(context_index())), esi);
497}
498
499
500void VirtualFrame::RestoreContextRegister() {
501 ASSERT(elements_[context_index()].is_memory());
502 __ mov(esi, Operand(ebp, fp_relative(context_index())));
503}
504
505
506void VirtualFrame::PushReceiverSlotAddress() {
507 Result temp = cgen_->allocator()->Allocate();
508 ASSERT(temp.is_valid());
509 __ lea(temp.reg(), ParameterAt(-1));
510 Push(&temp);
511}
512
513
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000514int VirtualFrame::InvalidateFrameSlotAt(int index) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000515 FrameElement original = elements_[index];
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000516
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000517 // Is this element the backing store of any copies?
518 int new_backing_index = kIllegalIndex;
519 if (original.is_copied()) {
520 // Verify it is copied, and find first copy.
521 for (int i = index + 1; i < elements_.length(); i++) {
522 if (elements_[i].is_copy() && elements_[i].index() == index) {
523 new_backing_index = i;
524 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000525 }
526 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000527 }
528
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000529 if (new_backing_index == kIllegalIndex) {
530 // No copies found, return kIllegalIndex.
531 if (original.is_register()) {
532 Unuse(original.reg());
533 }
534 elements_[index] = FrameElement::InvalidElement();
535 return kIllegalIndex;
536 }
537
538 // This is the backing store of copies.
539 Register backing_reg;
540 if (original.is_memory()) {
541 Result fresh = cgen_->allocator()->Allocate();
542 ASSERT(fresh.is_valid());
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000543 Use(fresh.reg(), new_backing_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000544 backing_reg = fresh.reg();
545 __ mov(backing_reg, Operand(ebp, fp_relative(index)));
546 } else {
547 // The original was in a register.
548 backing_reg = original.reg();
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000549 register_locations_[backing_reg.code()] = new_backing_index;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000550 }
551 // Invalidate the element at index.
552 elements_[index] = FrameElement::InvalidElement();
553 // Set the new backing element.
554 if (elements_[new_backing_index].is_synced()) {
555 elements_[new_backing_index] =
556 FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED);
557 } else {
558 elements_[new_backing_index] =
559 FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED);
560 }
561 // Update the other copies.
562 for (int i = new_backing_index + 1; i < elements_.length(); i++) {
563 if (elements_[i].is_copy() && elements_[i].index() == index) {
564 elements_[i].set_index(new_backing_index);
565 elements_[new_backing_index].set_copied();
566 }
567 }
568 return new_backing_index;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000569}
570
571
572void VirtualFrame::TakeFrameSlotAt(int index) {
573 ASSERT(index >= 0);
574 ASSERT(index <= elements_.length());
575 FrameElement original = elements_[index];
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000576 int new_backing_store_index = InvalidateFrameSlotAt(index);
577 if (new_backing_store_index != kIllegalIndex) {
578 elements_.Add(CopyElementAt(new_backing_store_index));
579 return;
580 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000581
582 switch (original.type()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000583 case FrameElement::MEMORY: {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000584 // Emit code to load the original element's data into a register.
585 // Push that register as a FrameElement on top of the frame.
586 Result fresh = cgen_->allocator()->Allocate();
587 ASSERT(fresh.is_valid());
588 FrameElement new_element =
589 FrameElement::RegisterElement(fresh.reg(),
590 FrameElement::NOT_SYNCED);
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000591 Use(fresh.reg(), elements_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000592 elements_.Add(new_element);
593 __ mov(fresh.reg(), Operand(ebp, fp_relative(index)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000594 break;
595 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000596 case FrameElement::REGISTER:
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000597 Use(original.reg(), elements_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000598 // Fall through.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000599 case FrameElement::CONSTANT:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000600 case FrameElement::COPY:
601 original.clear_sync();
602 elements_.Add(original);
603 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000604 case FrameElement::INVALID:
605 UNREACHABLE();
606 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000607 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000608}
609
610
611void VirtualFrame::StoreToFrameSlotAt(int index) {
612 // Store the value on top of the frame to the virtual frame slot at
613 // a given index. The value on top of the frame is left in place.
614 // This is a duplicating operation, so it can create copies.
615 ASSERT(index >= 0);
616 ASSERT(index < elements_.length());
617
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000618 int top_index = elements_.length() - 1;
619 FrameElement top = elements_[top_index];
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000620 FrameElement original = elements_[index];
621 if (top.is_copy() && top.index() == index) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000622 ASSERT(top.is_valid());
623
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000624 InvalidateFrameSlotAt(index);
625
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000626 if (top.is_copy()) {
627 // There are two cases based on the relative positions of the
628 // stored-to slot and the backing slot of the top element.
629 int backing_index = top.index();
630 ASSERT(backing_index != index);
631 if (backing_index < index) {
632 // 1. The top element is a copy of a slot below the stored-to
633 // slot. The stored-to slot becomes an unsynced copy of that
634 // same backing slot.
635 elements_[index] = CopyElementAt(backing_index);
636 } else {
637 // 2. The top element is a copy of a slot above the stored-to
638 // slot. The stored-to slot becomes the new (unsynced) backing
639 // slot and both the top element and the element at the former
640 // backing slot become copies of it. The sync state of the top
641 // and former backing elements is preserved.
642 FrameElement backing_element = elements_[backing_index];
643 ASSERT(backing_element.is_memory() || backing_element.is_register());
644 if (backing_element.is_memory()) {
645 // Because sets of copies are canonicalized to be backed by
646 // their lowest frame element, and because memory frame
647 // elements are backed by the corresponding stack address, we
648 // have to move the actual value down in the stack.
649 //
650 // TODO(209): considering allocating the stored-to slot to the
651 // temp register. Alternatively, allow copies to appear in
652 // any order in the frame and lazily move the value down to
653 // the slot.
654 Result temp = cgen_->allocator()->Allocate();
655 ASSERT(temp.is_valid());
656 __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index)));
657 __ mov(Operand(ebp, fp_relative(index)), temp.reg());
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000658 } else {
659 register_locations_[backing_element.reg().code()] = index;
660 if (backing_element.is_synced()) {
661 // If the element is a register, we will not actually move
662 // anything on the stack but only update the virtual frame
663 // element.
664 backing_element.clear_sync();
665 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000666 }
667 elements_[index] = backing_element;
668
669 // The old backing element becomes a copy of the new backing
670 // element.
671 FrameElement new_element = CopyElementAt(index);
672 elements_[backing_index] = new_element;
673 if (backing_element.is_synced()) {
674 elements_[backing_index].set_sync();
675 }
676
677 // All the copies of the old backing element (including the top
678 // element) become copies of the new backing element.
679 for (int i = backing_index + 1; i < elements_.length(); i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000680 if (elements_[i].is_copy() && elements_[i].index() == backing_index) {
681 elements_[i].set_index(index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000682 }
683 }
684 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000685 return;
686 }
687
688 // Move the top element to the stored-to slot and replace it (the
689 // top element) with a copy.
690 elements_[index] = top;
691 if (top.is_memory()) {
692 // TODO(209): consider allocating the stored-to slot to the temp
693 // register. Alternatively, allow copies to appear in any order
694 // in the frame and lazily move the value down to the slot.
695 FrameElement new_top = CopyElementAt(index);
696 new_top.set_sync();
697 elements_[top_index] = new_top;
698
699 // The sync state of the former top element is correct (synced).
700 // Emit code to move the value down in the frame.
701 Result temp = cgen_->allocator()->Allocate();
702 ASSERT(temp.is_valid());
703 __ mov(temp.reg(), Operand(esp, 0));
704 __ mov(Operand(ebp, fp_relative(index)), temp.reg());
705 } else if (top.is_register()) {
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000706 register_locations_[top.reg().code()] = index;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000707 // The stored-to slot has the (unsynced) register reference and
708 // the top element becomes a copy. The sync state of the top is
709 // preserved.
710 FrameElement new_top = CopyElementAt(index);
711 if (top.is_synced()) {
712 new_top.set_sync();
713 elements_[index].clear_sync();
714 }
715 elements_[top_index] = new_top;
716 } else {
717 // The stored-to slot holds the same value as the top but
718 // unsynced. (We do not have copies of constants yet.)
719 ASSERT(top.is_constant());
720 elements_[index].clear_sync();
721 }
722}
723
724
725void VirtualFrame::PushTryHandler(HandlerType type) {
726 ASSERT(cgen_->HasValidEntryRegisters());
727 // Grow the expression stack by handler size less two (the return address
728 // is already pushed by a call instruction, and PushTryHandler from the
729 // macro assembler will leave the top of stack in the eax register to be
730 // pushed separately).
731 Adjust(kHandlerSize - 2);
732 __ PushTryHandler(IN_JAVASCRIPT, type);
733 // TODO(1222589): remove the reliance of PushTryHandler on a cached TOS
734 EmitPush(eax);
735}
736
737
ager@chromium.org41826e72009-03-30 13:30:57 +0000738Result VirtualFrame::RawCallStub(CodeStub* stub) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000739 ASSERT(cgen_->HasValidEntryRegisters());
740 __ CallStub(stub);
741 Result result = cgen_->allocator()->Allocate(eax);
742 ASSERT(result.is_valid());
743 return result;
744}
745
746
ager@chromium.org41826e72009-03-30 13:30:57 +0000747Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) {
748 PrepareForCall(0, 0);
749 arg->ToRegister(eax);
750 arg->Unuse();
751 return RawCallStub(stub);
752}
753
754
755Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) {
756 PrepareForCall(0, 0);
757
758 if (arg0->is_register() && arg0->reg().is(eax)) {
759 if (arg1->is_register() && arg1->reg().is(edx)) {
760 // Wrong registers.
761 __ xchg(eax, edx);
762 } else {
763 // Register edx is free for arg0, which frees eax for arg1.
764 arg0->ToRegister(edx);
765 arg1->ToRegister(eax);
766 }
767 } else {
768 // Register eax is free for arg1, which guarantees edx is free for
769 // arg0.
770 arg1->ToRegister(eax);
771 arg0->ToRegister(edx);
772 }
773
774 arg0->Unuse();
775 arg1->Unuse();
776 return RawCallStub(stub);
777}
778
779
780Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) {
781 PrepareForCall(arg_count, arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000782 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org41826e72009-03-30 13:30:57 +0000783 __ CallRuntime(f, arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000784 Result result = cgen_->allocator()->Allocate(eax);
785 ASSERT(result.is_valid());
786 return result;
787}
788
789
ager@chromium.org41826e72009-03-30 13:30:57 +0000790Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
791 PrepareForCall(arg_count, arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000792 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org41826e72009-03-30 13:30:57 +0000793 __ CallRuntime(id, arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000794 Result result = cgen_->allocator()->Allocate(eax);
795 ASSERT(result.is_valid());
796 return result;
797}
798
799
800Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
801 InvokeFlag flag,
ager@chromium.org41826e72009-03-30 13:30:57 +0000802 int arg_count) {
803 PrepareForCall(arg_count, arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000804 ASSERT(cgen_->HasValidEntryRegisters());
805 __ InvokeBuiltin(id, flag);
806 Result result = cgen_->allocator()->Allocate(eax);
807 ASSERT(result.is_valid());
808 return result;
809}
810
811
812Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
813 RelocInfo::Mode rmode) {
814 ASSERT(cgen_->HasValidEntryRegisters());
815 __ call(code, rmode);
816 Result result = cgen_->allocator()->Allocate(eax);
817 ASSERT(result.is_valid());
818 return result;
819}
820
821
ager@chromium.org41826e72009-03-30 13:30:57 +0000822Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
823 // Name and receiver are on the top of the frame. The IC expects
824 // name in ecx and receiver on the stack. It does not drop the
825 // receiver.
826 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
827 Result name = Pop();
828 PrepareForCall(1, 0); // One stack arg, not callee-dropped.
829 name.ToRegister(ecx);
830 name.Unuse();
831 return RawCallCodeObject(ic, mode);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000832}
833
834
ager@chromium.org41826e72009-03-30 13:30:57 +0000835Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
836 // Key and receiver are on top of the frame. The IC expects them on
837 // the stack. It does not drop them.
838 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
839 PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
840 return RawCallCodeObject(ic, mode);
841}
842
843
844Result VirtualFrame::CallStoreIC() {
845 // Name, value, and receiver are on top of the frame. The IC
846 // expects name in ecx, value in eax, and receiver on the stack. It
847 // does not drop the receiver.
848 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
849 Result name = Pop();
850 Result value = Pop();
851 PrepareForCall(1, 0); // One stack arg, not callee-dropped.
852
853 if (value.is_register() && value.reg().is(ecx)) {
854 if (name.is_register() && name.reg().is(eax)) {
855 // Wrong registers.
856 __ xchg(eax, ecx);
857 } else {
858 // Register eax is free for value, which frees ecx for name.
859 value.ToRegister(eax);
860 name.ToRegister(ecx);
861 }
862 } else {
863 // Register ecx is free for name, which guarantees eax is free for
864 // value.
865 name.ToRegister(ecx);
866 value.ToRegister(eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000867 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000868
869 name.Unuse();
870 value.Unuse();
871 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
872}
873
874
875Result VirtualFrame::CallKeyedStoreIC() {
876 // Value, key, and receiver are on the top of the frame. The IC
877 // expects value in eax and key and receiver on the stack. It does
878 // not drop the key and receiver.
879 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
880 // TODO(1222589): Make the IC grab the values from the stack.
881 Result value = Pop();
882 PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
883 value.ToRegister(eax);
884 value.Unuse();
885 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
886}
887
888
889Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
890 int arg_count,
891 int loop_nesting) {
892 // Arguments, receiver, and function name are on top of the frame.
893 // The IC expects them on the stack. It does not drop the function
894 // name slot (but it does drop the rest).
895 Handle<Code> ic = (loop_nesting > 0)
896 ? cgen_->ComputeCallInitializeInLoop(arg_count)
897 : cgen_->ComputeCallInitialize(arg_count);
898 // Spill args, receiver, and function. The call will drop args and
899 // receiver.
900 PrepareForCall(arg_count + 2, arg_count + 1);
901 return RawCallCodeObject(ic, mode);
902}
903
904
905Result VirtualFrame::CallConstructor(int arg_count) {
906 // Arguments, receiver, and function are on top of the frame. The
907 // IC expects arg count in eax, function in edi, and the arguments
908 // and receiver on the stack.
909 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
910 // Duplicate the function before preparing the frame.
911 PushElementAt(arg_count + 1);
912 Result function = Pop();
913 PrepareForCall(arg_count + 1, arg_count + 1); // Spill args and receiver.
914 function.ToRegister(edi);
915
916 // Constructors are called with the number of arguments in register
917 // eax for now. Another option would be to have separate construct
918 // call trampolines per different arguments counts encountered.
919 Result num_args = cgen_->allocator()->Allocate(eax);
920 ASSERT(num_args.is_valid());
921 __ Set(num_args.reg(), Immediate(arg_count));
922
923 function.Unuse();
924 num_args.Unuse();
925 return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000926}
927
928
929void VirtualFrame::Drop(int count) {
930 ASSERT(height() >= count);
931 int num_virtual_elements = (elements_.length() - 1) - stack_pointer_;
932
933 // Emit code to lower the stack pointer if necessary.
934 if (num_virtual_elements < count) {
935 int num_dropped = count - num_virtual_elements;
936 stack_pointer_ -= num_dropped;
937 __ add(Operand(esp), Immediate(num_dropped * kPointerSize));
938 }
939
940 // Discard elements from the virtual frame and free any registers.
941 for (int i = 0; i < count; i++) {
942 FrameElement dropped = elements_.RemoveLast();
943 if (dropped.is_register()) {
944 Unuse(dropped.reg());
945 }
946 }
947}
948
949
950Result VirtualFrame::Pop() {
951 FrameElement element = elements_.RemoveLast();
952 int index = elements_.length();
953 ASSERT(element.is_valid());
954
955 bool pop_needed = (stack_pointer_ == index);
956 if (pop_needed) {
957 stack_pointer_--;
958 if (element.is_memory()) {
959 Result temp = cgen_->allocator()->Allocate();
960 ASSERT(temp.is_valid());
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000961 temp.set_static_type(element.static_type());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000962 __ pop(temp.reg());
963 return temp;
964 }
965
966 __ add(Operand(esp), Immediate(kPointerSize));
967 }
968 ASSERT(!element.is_memory());
969
970 // The top element is a register, constant, or a copy. Unuse
971 // registers and follow copies to their backing store.
972 if (element.is_register()) {
973 Unuse(element.reg());
974 } else if (element.is_copy()) {
975 ASSERT(element.index() < index);
976 index = element.index();
977 element = elements_[index];
978 }
979 ASSERT(!element.is_copy());
980
981 // The element is memory, a register, or a constant.
982 if (element.is_memory()) {
983 // Memory elements could only be the backing store of a copy.
984 // Allocate the original to a register.
985 ASSERT(index <= stack_pointer_);
986 Result temp = cgen_->allocator()->Allocate();
987 ASSERT(temp.is_valid());
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000988 Use(temp.reg(), index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000989 FrameElement new_element =
990 FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
kmillikin@chromium.orga1abb4d2009-03-31 16:07:48 +0000991 // Preserve the copy flag on the element.
992 if (element.is_copied()) new_element.set_copied();
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000993 new_element.set_static_type(element.static_type());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000994 elements_[index] = new_element;
995 __ mov(temp.reg(), Operand(ebp, fp_relative(index)));
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000996 return Result(temp.reg(), cgen_, element.static_type());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000997 } else if (element.is_register()) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000998 return Result(element.reg(), cgen_, element.static_type());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000999 } else {
1000 ASSERT(element.is_constant());
1001 return Result(element.handle(), cgen_);
1002 }
1003}
1004
1005
1006void VirtualFrame::EmitPop(Register reg) {
1007 ASSERT(stack_pointer_ == elements_.length() - 1);
1008 stack_pointer_--;
1009 elements_.RemoveLast();
1010 __ pop(reg);
1011}
1012
1013
1014void VirtualFrame::EmitPop(Operand operand) {
1015 ASSERT(stack_pointer_ == elements_.length() - 1);
1016 stack_pointer_--;
1017 elements_.RemoveLast();
1018 __ pop(operand);
1019}
1020
1021
1022void VirtualFrame::EmitPush(Register reg) {
1023 ASSERT(stack_pointer_ == elements_.length() - 1);
1024 elements_.Add(FrameElement::MemoryElement());
1025 stack_pointer_++;
1026 __ push(reg);
1027}
1028
1029
1030void VirtualFrame::EmitPush(Operand operand) {
1031 ASSERT(stack_pointer_ == elements_.length() - 1);
1032 elements_.Add(FrameElement::MemoryElement());
1033 stack_pointer_++;
1034 __ push(operand);
1035}
1036
1037
1038void VirtualFrame::EmitPush(Immediate immediate) {
1039 ASSERT(stack_pointer_ == elements_.length() - 1);
1040 elements_.Add(FrameElement::MemoryElement());
1041 stack_pointer_++;
1042 __ push(immediate);
1043}
1044
1045
1046#undef __
1047
1048} } // namespace v8::internal