OLD | NEW |
| (Empty) |
1 // 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 | |
28 #include "v8.h" | |
29 | |
30 #if defined(V8_TARGET_ARCH_IA32) | |
31 | |
32 #include "codegen-inl.h" | |
33 #include "register-allocator-inl.h" | |
34 #include "scopes.h" | |
35 #include "virtual-frame-inl.h" | |
36 #include "stub-cache.h" | |
37 | |
38 namespace v8 { | |
39 namespace internal { | |
40 | |
41 #define __ ACCESS_MASM(masm()) | |
42 | |
43 void VirtualFrame::SyncElementBelowStackPointer(int index) { | |
44 // Emit code to write elements below the stack pointer to their | |
45 // (already allocated) stack address. | |
46 ASSERT(index <= stack_pointer_); | |
47 FrameElement element = elements_[index]; | |
48 ASSERT(!element.is_synced()); | |
49 switch (element.type()) { | |
50 case FrameElement::INVALID: | |
51 break; | |
52 | |
53 case FrameElement::MEMORY: | |
54 // This function should not be called with synced elements. | |
55 // (memory elements are always synced). | |
56 UNREACHABLE(); | |
57 break; | |
58 | |
59 case FrameElement::REGISTER: | |
60 __ mov(Operand(ebp, fp_relative(index)), element.reg()); | |
61 break; | |
62 | |
63 case FrameElement::CONSTANT: | |
64 if (cgen()->IsUnsafeSmi(element.handle())) { | |
65 cgen()->StoreUnsafeSmiToLocal(fp_relative(index), element.handle()); | |
66 } else { | |
67 __ Set(Operand(ebp, fp_relative(index)), | |
68 Immediate(element.handle())); | |
69 } | |
70 break; | |
71 | |
72 case FrameElement::COPY: { | |
73 int backing_index = element.index(); | |
74 FrameElement backing_element = elements_[backing_index]; | |
75 if (backing_element.is_memory()) { | |
76 Result temp = cgen()->allocator()->Allocate(); | |
77 ASSERT(temp.is_valid()); | |
78 __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index))); | |
79 __ mov(Operand(ebp, fp_relative(index)), temp.reg()); | |
80 } else { | |
81 ASSERT(backing_element.is_register()); | |
82 __ mov(Operand(ebp, fp_relative(index)), backing_element.reg()); | |
83 } | |
84 break; | |
85 } | |
86 } | |
87 elements_[index].set_sync(); | |
88 } | |
89 | |
90 | |
91 void VirtualFrame::SyncElementByPushing(int index) { | |
92 // Sync an element of the frame that is just above the stack pointer | |
93 // by pushing it. | |
94 ASSERT(index == stack_pointer_ + 1); | |
95 stack_pointer_++; | |
96 FrameElement element = elements_[index]; | |
97 | |
98 switch (element.type()) { | |
99 case FrameElement::INVALID: | |
100 __ push(Immediate(Smi::FromInt(0))); | |
101 break; | |
102 | |
103 case FrameElement::MEMORY: | |
104 // No memory elements exist above the stack pointer. | |
105 UNREACHABLE(); | |
106 break; | |
107 | |
108 case FrameElement::REGISTER: | |
109 __ push(element.reg()); | |
110 break; | |
111 | |
112 case FrameElement::CONSTANT: | |
113 if (cgen()->IsUnsafeSmi(element.handle())) { | |
114 cgen()->PushUnsafeSmi(element.handle()); | |
115 } else { | |
116 __ push(Immediate(element.handle())); | |
117 } | |
118 break; | |
119 | |
120 case FrameElement::COPY: { | |
121 int backing_index = element.index(); | |
122 FrameElement backing = elements_[backing_index]; | |
123 ASSERT(backing.is_memory() || backing.is_register()); | |
124 if (backing.is_memory()) { | |
125 __ push(Operand(ebp, fp_relative(backing_index))); | |
126 } else { | |
127 __ push(backing.reg()); | |
128 } | |
129 break; | |
130 } | |
131 } | |
132 elements_[index].set_sync(); | |
133 } | |
134 | |
135 | |
136 // Clear the dirty bits for the range of elements in | |
137 // [min(stack_pointer_ + 1,begin), end]. | |
138 void VirtualFrame::SyncRange(int begin, int end) { | |
139 ASSERT(begin >= 0); | |
140 ASSERT(end < element_count()); | |
141 // Sync elements below the range if they have not been materialized | |
142 // on the stack. | |
143 int start = Min(begin, stack_pointer_ + 1); | |
144 | |
145 // Emit normal push instructions for elements above stack pointer | |
146 // and use mov instructions if we are below stack pointer. | |
147 for (int i = start; i <= end; i++) { | |
148 if (!elements_[i].is_synced()) { | |
149 if (i <= stack_pointer_) { | |
150 SyncElementBelowStackPointer(i); | |
151 } else { | |
152 SyncElementByPushing(i); | |
153 } | |
154 } | |
155 } | |
156 } | |
157 | |
158 | |
159 void VirtualFrame::MakeMergable() { | |
160 for (int i = 0; i < element_count(); i++) { | |
161 FrameElement element = elements_[i]; | |
162 | |
163 // All number type information is reset to unknown for a mergable frame | |
164 // because of incoming back edges. | |
165 if (element.is_constant() || element.is_copy()) { | |
166 if (element.is_synced()) { | |
167 // Just spill. | |
168 elements_[i] = FrameElement::MemoryElement(TypeInfo::Unknown()); | |
169 } else { | |
170 // Allocate to a register. | |
171 FrameElement backing_element; // Invalid if not a copy. | |
172 if (element.is_copy()) { | |
173 backing_element = elements_[element.index()]; | |
174 } | |
175 Result fresh = cgen()->allocator()->Allocate(); | |
176 ASSERT(fresh.is_valid()); // A register was spilled if all were in use. | |
177 elements_[i] = | |
178 FrameElement::RegisterElement(fresh.reg(), | |
179 FrameElement::NOT_SYNCED, | |
180 TypeInfo::Unknown()); | |
181 Use(fresh.reg(), i); | |
182 | |
183 // Emit a move. | |
184 if (element.is_constant()) { | |
185 if (cgen()->IsUnsafeSmi(element.handle())) { | |
186 cgen()->MoveUnsafeSmi(fresh.reg(), element.handle()); | |
187 } else { | |
188 __ Set(fresh.reg(), Immediate(element.handle())); | |
189 } | |
190 } else { | |
191 ASSERT(element.is_copy()); | |
192 // Copies are only backed by register or memory locations. | |
193 if (backing_element.is_register()) { | |
194 // The backing store may have been spilled by allocating, | |
195 // but that's OK. If it was, the value is right where we | |
196 // want it. | |
197 if (!fresh.reg().is(backing_element.reg())) { | |
198 __ mov(fresh.reg(), backing_element.reg()); | |
199 } | |
200 } else { | |
201 ASSERT(backing_element.is_memory()); | |
202 __ mov(fresh.reg(), Operand(ebp, fp_relative(element.index()))); | |
203 } | |
204 } | |
205 } | |
206 // No need to set the copied flag --- there are no copies. | |
207 } else { | |
208 // Clear the copy flag of non-constant, non-copy elements. | |
209 // They cannot be copied because copies are not allowed. | |
210 // The copy flag is not relied on before the end of this loop, | |
211 // including when registers are spilled. | |
212 elements_[i].clear_copied(); | |
213 elements_[i].set_type_info(TypeInfo::Unknown()); | |
214 } | |
215 } | |
216 } | |
217 | |
218 | |
219 void VirtualFrame::MergeTo(VirtualFrame* expected) { | |
220 Comment cmnt(masm(), "[ Merge frame"); | |
221 // We should always be merging the code generator's current frame to an | |
222 // expected frame. | |
223 ASSERT(cgen()->frame() == this); | |
224 | |
225 // Adjust the stack pointer upward (toward the top of the virtual | |
226 // frame) if necessary. | |
227 if (stack_pointer_ < expected->stack_pointer_) { | |
228 int difference = expected->stack_pointer_ - stack_pointer_; | |
229 stack_pointer_ = expected->stack_pointer_; | |
230 __ sub(Operand(esp), Immediate(difference * kPointerSize)); | |
231 } | |
232 | |
233 MergeMoveRegistersToMemory(expected); | |
234 MergeMoveRegistersToRegisters(expected); | |
235 MergeMoveMemoryToRegisters(expected); | |
236 | |
237 // Adjust the stack pointer downward if necessary. | |
238 if (stack_pointer_ > expected->stack_pointer_) { | |
239 int difference = stack_pointer_ - expected->stack_pointer_; | |
240 stack_pointer_ = expected->stack_pointer_; | |
241 __ add(Operand(esp), Immediate(difference * kPointerSize)); | |
242 } | |
243 | |
244 // At this point, the frames should be identical. | |
245 ASSERT(Equals(expected)); | |
246 } | |
247 | |
248 | |
249 void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { | |
250 ASSERT(stack_pointer_ >= expected->stack_pointer_); | |
251 | |
252 // Move registers, constants, and copies to memory. Perform moves | |
253 // from the top downward in the frame in order to leave the backing | |
254 // stores of copies in registers. | |
255 // | |
256 // Moving memory-backed copies to memory requires a spare register | |
257 // for the memory-to-memory moves. Since we are performing a merge, | |
258 // we use esi (which is already saved in the frame). We keep track | |
259 // of the index of the frame element esi is caching or kIllegalIndex | |
260 // if esi has not been disturbed. | |
261 int esi_caches = kIllegalIndex; | |
262 for (int i = element_count() - 1; i >= 0; i--) { | |
263 FrameElement target = expected->elements_[i]; | |
264 if (target.is_register()) continue; // Handle registers later. | |
265 if (target.is_memory()) { | |
266 FrameElement source = elements_[i]; | |
267 switch (source.type()) { | |
268 case FrameElement::INVALID: | |
269 // Not a legal merge move. | |
270 UNREACHABLE(); | |
271 break; | |
272 | |
273 case FrameElement::MEMORY: | |
274 // Already in place. | |
275 break; | |
276 | |
277 case FrameElement::REGISTER: | |
278 Unuse(source.reg()); | |
279 if (!source.is_synced()) { | |
280 __ mov(Operand(ebp, fp_relative(i)), source.reg()); | |
281 } | |
282 break; | |
283 | |
284 case FrameElement::CONSTANT: | |
285 if (!source.is_synced()) { | |
286 if (cgen()->IsUnsafeSmi(source.handle())) { | |
287 esi_caches = i; | |
288 cgen()->MoveUnsafeSmi(esi, source.handle()); | |
289 __ mov(Operand(ebp, fp_relative(i)), esi); | |
290 } else { | |
291 __ Set(Operand(ebp, fp_relative(i)), Immediate(source.handle())); | |
292 } | |
293 } | |
294 break; | |
295 | |
296 case FrameElement::COPY: | |
297 if (!source.is_synced()) { | |
298 int backing_index = source.index(); | |
299 FrameElement backing_element = elements_[backing_index]; | |
300 if (backing_element.is_memory()) { | |
301 // If we have to spill a register, we spill esi. | |
302 if (esi_caches != backing_index) { | |
303 esi_caches = backing_index; | |
304 __ mov(esi, Operand(ebp, fp_relative(backing_index))); | |
305 } | |
306 __ mov(Operand(ebp, fp_relative(i)), esi); | |
307 } else { | |
308 ASSERT(backing_element.is_register()); | |
309 __ mov(Operand(ebp, fp_relative(i)), backing_element.reg()); | |
310 } | |
311 } | |
312 break; | |
313 } | |
314 } | |
315 elements_[i] = target; | |
316 } | |
317 | |
318 if (esi_caches != kIllegalIndex) { | |
319 __ mov(esi, Operand(ebp, fp_relative(context_index()))); | |
320 } | |
321 } | |
322 | |
323 | |
324 void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) { | |
325 // We have already done X-to-memory moves. | |
326 ASSERT(stack_pointer_ >= expected->stack_pointer_); | |
327 | |
328 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { | |
329 // Move the right value into register i if it is currently in a register. | |
330 int index = expected->register_location(i); | |
331 int use_index = register_location(i); | |
332 // Skip if register i is unused in the target or else if source is | |
333 // not a register (this is not a register-to-register move). | |
334 if (index == kIllegalIndex || !elements_[index].is_register()) continue; | |
335 | |
336 Register target = RegisterAllocator::ToRegister(i); | |
337 Register source = elements_[index].reg(); | |
338 if (index != use_index) { | |
339 if (use_index == kIllegalIndex) { // Target is currently unused. | |
340 // Copy contents of source from source to target. | |
341 // Set frame element register to target. | |
342 Use(target, index); | |
343 Unuse(source); | |
344 __ mov(target, source); | |
345 } else { | |
346 // Exchange contents of registers source and target. | |
347 // Nothing except the register backing use_index has changed. | |
348 elements_[use_index].set_reg(source); | |
349 set_register_location(target, index); | |
350 set_register_location(source, use_index); | |
351 __ xchg(source, target); | |
352 } | |
353 } | |
354 | |
355 if (!elements_[index].is_synced() && | |
356 expected->elements_[index].is_synced()) { | |
357 __ mov(Operand(ebp, fp_relative(index)), target); | |
358 } | |
359 elements_[index] = expected->elements_[index]; | |
360 } | |
361 } | |
362 | |
363 | |
364 void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) { | |
365 // Move memory, constants, and copies to registers. This is the | |
366 // final step and since it is not done from the bottom up, but in | |
367 // register code order, we have special code to ensure that the backing | |
368 // elements of copies are in their correct locations when we | |
369 // encounter the copies. | |
370 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { | |
371 int index = expected->register_location(i); | |
372 if (index != kIllegalIndex) { | |
373 FrameElement source = elements_[index]; | |
374 FrameElement target = expected->elements_[index]; | |
375 Register target_reg = RegisterAllocator::ToRegister(i); | |
376 ASSERT(target.reg().is(target_reg)); | |
377 switch (source.type()) { | |
378 case FrameElement::INVALID: // Fall through. | |
379 UNREACHABLE(); | |
380 break; | |
381 case FrameElement::REGISTER: | |
382 ASSERT(source.Equals(target)); | |
383 // Go to next iteration. Skips Use(target_reg) and syncing | |
384 // below. It is safe to skip syncing because a target | |
385 // register frame element would only be synced if all source | |
386 // elements were. | |
387 continue; | |
388 break; | |
389 case FrameElement::MEMORY: | |
390 ASSERT(index <= stack_pointer_); | |
391 __ mov(target_reg, Operand(ebp, fp_relative(index))); | |
392 break; | |
393 | |
394 case FrameElement::CONSTANT: | |
395 if (cgen()->IsUnsafeSmi(source.handle())) { | |
396 cgen()->MoveUnsafeSmi(target_reg, source.handle()); | |
397 } else { | |
398 __ Set(target_reg, Immediate(source.handle())); | |
399 } | |
400 break; | |
401 | |
402 case FrameElement::COPY: { | |
403 int backing_index = source.index(); | |
404 FrameElement backing = elements_[backing_index]; | |
405 ASSERT(backing.is_memory() || backing.is_register()); | |
406 if (backing.is_memory()) { | |
407 ASSERT(backing_index <= stack_pointer_); | |
408 // Code optimization if backing store should also move | |
409 // to a register: move backing store to its register first. | |
410 if (expected->elements_[backing_index].is_register()) { | |
411 FrameElement new_backing = expected->elements_[backing_index]; | |
412 Register new_backing_reg = new_backing.reg(); | |
413 ASSERT(!is_used(new_backing_reg)); | |
414 elements_[backing_index] = new_backing; | |
415 Use(new_backing_reg, backing_index); | |
416 __ mov(new_backing_reg, | |
417 Operand(ebp, fp_relative(backing_index))); | |
418 __ mov(target_reg, new_backing_reg); | |
419 } else { | |
420 __ mov(target_reg, Operand(ebp, fp_relative(backing_index))); | |
421 } | |
422 } else { | |
423 __ mov(target_reg, backing.reg()); | |
424 } | |
425 } | |
426 } | |
427 // Ensure the proper sync state. | |
428 if (target.is_synced() && !source.is_synced()) { | |
429 __ mov(Operand(ebp, fp_relative(index)), target_reg); | |
430 } | |
431 Use(target_reg, index); | |
432 elements_[index] = target; | |
433 } | |
434 } | |
435 } | |
436 | |
437 | |
438 void VirtualFrame::Enter() { | |
439 // Registers live on entry: esp, ebp, esi, edi. | |
440 Comment cmnt(masm(), "[ Enter JS frame"); | |
441 | |
442 #ifdef DEBUG | |
443 if (FLAG_debug_code) { | |
444 // Verify that edi contains a JS function. The following code | |
445 // relies on eax being available for use. | |
446 __ test(edi, Immediate(kSmiTagMask)); | |
447 __ Check(not_zero, | |
448 "VirtualFrame::Enter - edi is not a function (smi check)."); | |
449 __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); | |
450 __ Check(equal, | |
451 "VirtualFrame::Enter - edi is not a function (map check)."); | |
452 } | |
453 #endif | |
454 | |
455 EmitPush(ebp); | |
456 | |
457 __ mov(ebp, Operand(esp)); | |
458 | |
459 // Store the context in the frame. The context is kept in esi and a | |
460 // copy is stored in the frame. The external reference to esi | |
461 // remains. | |
462 EmitPush(esi); | |
463 | |
464 // Store the function in the frame. The frame owns the register | |
465 // reference now (ie, it can keep it in edi or spill it later). | |
466 Push(edi); | |
467 SyncElementAt(element_count() - 1); | |
468 cgen()->allocator()->Unuse(edi); | |
469 } | |
470 | |
471 | |
472 void VirtualFrame::Exit() { | |
473 Comment cmnt(masm(), "[ Exit JS frame"); | |
474 // Record the location of the JS exit code for patching when setting | |
475 // break point. | |
476 __ RecordJSReturn(); | |
477 | |
478 // Avoid using the leave instruction here, because it is too | |
479 // short. We need the return sequence to be a least the size of a | |
480 // call instruction to support patching the exit code in the | |
481 // debugger. See VisitReturnStatement for the full return sequence. | |
482 __ mov(esp, Operand(ebp)); | |
483 stack_pointer_ = frame_pointer(); | |
484 for (int i = element_count() - 1; i > stack_pointer_; i--) { | |
485 FrameElement last = elements_.RemoveLast(); | |
486 if (last.is_register()) { | |
487 Unuse(last.reg()); | |
488 } | |
489 } | |
490 | |
491 EmitPop(ebp); | |
492 } | |
493 | |
494 | |
495 void VirtualFrame::AllocateStackSlots() { | |
496 int count = local_count(); | |
497 if (count > 0) { | |
498 Comment cmnt(masm(), "[ Allocate space for locals"); | |
499 // The locals are initialized to a constant (the undefined value), but | |
500 // we sync them with the actual frame to allocate space for spilling | |
501 // them later. First sync everything above the stack pointer so we can | |
502 // use pushes to allocate and initialize the locals. | |
503 SyncRange(stack_pointer_ + 1, element_count() - 1); | |
504 Handle<Object> undefined = FACTORY->undefined_value(); | |
505 FrameElement initial_value = | |
506 FrameElement::ConstantElement(undefined, FrameElement::SYNCED); | |
507 if (count == 1) { | |
508 __ push(Immediate(undefined)); | |
509 } else if (count < kLocalVarBound) { | |
510 // For less locals the unrolled loop is more compact. | |
511 Result temp = cgen()->allocator()->Allocate(); | |
512 ASSERT(temp.is_valid()); | |
513 __ Set(temp.reg(), Immediate(undefined)); | |
514 for (int i = 0; i < count; i++) { | |
515 __ push(temp.reg()); | |
516 } | |
517 } else { | |
518 // For more locals a loop in generated code is more compact. | |
519 Label alloc_locals_loop; | |
520 Result cnt = cgen()->allocator()->Allocate(); | |
521 Result tmp = cgen()->allocator()->Allocate(); | |
522 ASSERT(cnt.is_valid()); | |
523 ASSERT(tmp.is_valid()); | |
524 __ mov(cnt.reg(), Immediate(count)); | |
525 __ mov(tmp.reg(), Immediate(undefined)); | |
526 __ bind(&alloc_locals_loop); | |
527 __ push(tmp.reg()); | |
528 __ dec(cnt.reg()); | |
529 __ j(not_zero, &alloc_locals_loop); | |
530 } | |
531 for (int i = 0; i < count; i++) { | |
532 elements_.Add(initial_value); | |
533 stack_pointer_++; | |
534 } | |
535 } | |
536 } | |
537 | |
538 | |
539 void VirtualFrame::SaveContextRegister() { | |
540 ASSERT(elements_[context_index()].is_memory()); | |
541 __ mov(Operand(ebp, fp_relative(context_index())), esi); | |
542 } | |
543 | |
544 | |
545 void VirtualFrame::RestoreContextRegister() { | |
546 ASSERT(elements_[context_index()].is_memory()); | |
547 __ mov(esi, Operand(ebp, fp_relative(context_index()))); | |
548 } | |
549 | |
550 | |
551 void VirtualFrame::PushReceiverSlotAddress() { | |
552 Result temp = cgen()->allocator()->Allocate(); | |
553 ASSERT(temp.is_valid()); | |
554 __ lea(temp.reg(), ParameterAt(-1)); | |
555 Push(&temp); | |
556 } | |
557 | |
558 | |
559 int VirtualFrame::InvalidateFrameSlotAt(int index) { | |
560 FrameElement original = elements_[index]; | |
561 | |
562 // Is this element the backing store of any copies? | |
563 int new_backing_index = kIllegalIndex; | |
564 if (original.is_copied()) { | |
565 // Verify it is copied, and find first copy. | |
566 for (int i = index + 1; i < element_count(); i++) { | |
567 if (elements_[i].is_copy() && elements_[i].index() == index) { | |
568 new_backing_index = i; | |
569 break; | |
570 } | |
571 } | |
572 } | |
573 | |
574 if (new_backing_index == kIllegalIndex) { | |
575 // No copies found, return kIllegalIndex. | |
576 if (original.is_register()) { | |
577 Unuse(original.reg()); | |
578 } | |
579 elements_[index] = FrameElement::InvalidElement(); | |
580 return kIllegalIndex; | |
581 } | |
582 | |
583 // This is the backing store of copies. | |
584 Register backing_reg; | |
585 if (original.is_memory()) { | |
586 Result fresh = cgen()->allocator()->Allocate(); | |
587 ASSERT(fresh.is_valid()); | |
588 Use(fresh.reg(), new_backing_index); | |
589 backing_reg = fresh.reg(); | |
590 __ mov(backing_reg, Operand(ebp, fp_relative(index))); | |
591 } else { | |
592 // The original was in a register. | |
593 backing_reg = original.reg(); | |
594 set_register_location(backing_reg, new_backing_index); | |
595 } | |
596 // Invalidate the element at index. | |
597 elements_[index] = FrameElement::InvalidElement(); | |
598 // Set the new backing element. | |
599 if (elements_[new_backing_index].is_synced()) { | |
600 elements_[new_backing_index] = | |
601 FrameElement::RegisterElement(backing_reg, | |
602 FrameElement::SYNCED, | |
603 original.type_info()); | |
604 } else { | |
605 elements_[new_backing_index] = | |
606 FrameElement::RegisterElement(backing_reg, | |
607 FrameElement::NOT_SYNCED, | |
608 original.type_info()); | |
609 } | |
610 // Update the other copies. | |
611 for (int i = new_backing_index + 1; i < element_count(); i++) { | |
612 if (elements_[i].is_copy() && elements_[i].index() == index) { | |
613 elements_[i].set_index(new_backing_index); | |
614 elements_[new_backing_index].set_copied(); | |
615 } | |
616 } | |
617 return new_backing_index; | |
618 } | |
619 | |
620 | |
621 void VirtualFrame::TakeFrameSlotAt(int index) { | |
622 ASSERT(index >= 0); | |
623 ASSERT(index <= element_count()); | |
624 FrameElement original = elements_[index]; | |
625 int new_backing_store_index = InvalidateFrameSlotAt(index); | |
626 if (new_backing_store_index != kIllegalIndex) { | |
627 elements_.Add(CopyElementAt(new_backing_store_index)); | |
628 return; | |
629 } | |
630 | |
631 switch (original.type()) { | |
632 case FrameElement::MEMORY: { | |
633 // Emit code to load the original element's data into a register. | |
634 // Push that register as a FrameElement on top of the frame. | |
635 Result fresh = cgen()->allocator()->Allocate(); | |
636 ASSERT(fresh.is_valid()); | |
637 FrameElement new_element = | |
638 FrameElement::RegisterElement(fresh.reg(), | |
639 FrameElement::NOT_SYNCED, | |
640 original.type_info()); | |
641 Use(fresh.reg(), element_count()); | |
642 elements_.Add(new_element); | |
643 __ mov(fresh.reg(), Operand(ebp, fp_relative(index))); | |
644 break; | |
645 } | |
646 case FrameElement::REGISTER: | |
647 Use(original.reg(), element_count()); | |
648 // Fall through. | |
649 case FrameElement::CONSTANT: | |
650 case FrameElement::COPY: | |
651 original.clear_sync(); | |
652 elements_.Add(original); | |
653 break; | |
654 case FrameElement::INVALID: | |
655 UNREACHABLE(); | |
656 break; | |
657 } | |
658 } | |
659 | |
660 | |
661 void VirtualFrame::StoreToFrameSlotAt(int index) { | |
662 // Store the value on top of the frame to the virtual frame slot at | |
663 // a given index. The value on top of the frame is left in place. | |
664 // This is a duplicating operation, so it can create copies. | |
665 ASSERT(index >= 0); | |
666 ASSERT(index < element_count()); | |
667 | |
668 int top_index = element_count() - 1; | |
669 FrameElement top = elements_[top_index]; | |
670 FrameElement original = elements_[index]; | |
671 if (top.is_copy() && top.index() == index) return; | |
672 ASSERT(top.is_valid()); | |
673 | |
674 InvalidateFrameSlotAt(index); | |
675 | |
676 // InvalidateFrameSlotAt can potentially change any frame element, due | |
677 // to spilling registers to allocate temporaries in order to preserve | |
678 // the copy-on-write semantics of aliased elements. Reload top from | |
679 // the frame. | |
680 top = elements_[top_index]; | |
681 | |
682 if (top.is_copy()) { | |
683 // There are two cases based on the relative positions of the | |
684 // stored-to slot and the backing slot of the top element. | |
685 int backing_index = top.index(); | |
686 ASSERT(backing_index != index); | |
687 if (backing_index < index) { | |
688 // 1. The top element is a copy of a slot below the stored-to | |
689 // slot. The stored-to slot becomes an unsynced copy of that | |
690 // same backing slot. | |
691 elements_[index] = CopyElementAt(backing_index); | |
692 } else { | |
693 // 2. The top element is a copy of a slot above the stored-to | |
694 // slot. The stored-to slot becomes the new (unsynced) backing | |
695 // slot and both the top element and the element at the former | |
696 // backing slot become copies of it. The sync state of the top | |
697 // and former backing elements is preserved. | |
698 FrameElement backing_element = elements_[backing_index]; | |
699 ASSERT(backing_element.is_memory() || backing_element.is_register()); | |
700 if (backing_element.is_memory()) { | |
701 // Because sets of copies are canonicalized to be backed by | |
702 // their lowest frame element, and because memory frame | |
703 // elements are backed by the corresponding stack address, we | |
704 // have to move the actual value down in the stack. | |
705 // | |
706 // TODO(209): considering allocating the stored-to slot to the | |
707 // temp register. Alternatively, allow copies to appear in | |
708 // any order in the frame and lazily move the value down to | |
709 // the slot. | |
710 Result temp = cgen()->allocator()->Allocate(); | |
711 ASSERT(temp.is_valid()); | |
712 __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index))); | |
713 __ mov(Operand(ebp, fp_relative(index)), temp.reg()); | |
714 } else { | |
715 set_register_location(backing_element.reg(), index); | |
716 if (backing_element.is_synced()) { | |
717 // If the element is a register, we will not actually move | |
718 // anything on the stack but only update the virtual frame | |
719 // element. | |
720 backing_element.clear_sync(); | |
721 } | |
722 } | |
723 elements_[index] = backing_element; | |
724 | |
725 // The old backing element becomes a copy of the new backing | |
726 // element. | |
727 FrameElement new_element = CopyElementAt(index); | |
728 elements_[backing_index] = new_element; | |
729 if (backing_element.is_synced()) { | |
730 elements_[backing_index].set_sync(); | |
731 } | |
732 | |
733 // All the copies of the old backing element (including the top | |
734 // element) become copies of the new backing element. | |
735 for (int i = backing_index + 1; i < element_count(); i++) { | |
736 if (elements_[i].is_copy() && elements_[i].index() == backing_index) { | |
737 elements_[i].set_index(index); | |
738 } | |
739 } | |
740 } | |
741 return; | |
742 } | |
743 | |
744 // Move the top element to the stored-to slot and replace it (the | |
745 // top element) with a copy. | |
746 elements_[index] = top; | |
747 if (top.is_memory()) { | |
748 // TODO(209): consider allocating the stored-to slot to the temp | |
749 // register. Alternatively, allow copies to appear in any order | |
750 // in the frame and lazily move the value down to the slot. | |
751 FrameElement new_top = CopyElementAt(index); | |
752 new_top.set_sync(); | |
753 elements_[top_index] = new_top; | |
754 | |
755 // The sync state of the former top element is correct (synced). | |
756 // Emit code to move the value down in the frame. | |
757 Result temp = cgen()->allocator()->Allocate(); | |
758 ASSERT(temp.is_valid()); | |
759 __ mov(temp.reg(), Operand(esp, 0)); | |
760 __ mov(Operand(ebp, fp_relative(index)), temp.reg()); | |
761 } else if (top.is_register()) { | |
762 set_register_location(top.reg(), index); | |
763 // The stored-to slot has the (unsynced) register reference and | |
764 // the top element becomes a copy. The sync state of the top is | |
765 // preserved. | |
766 FrameElement new_top = CopyElementAt(index); | |
767 if (top.is_synced()) { | |
768 new_top.set_sync(); | |
769 elements_[index].clear_sync(); | |
770 } | |
771 elements_[top_index] = new_top; | |
772 } else { | |
773 // The stored-to slot holds the same value as the top but | |
774 // unsynced. (We do not have copies of constants yet.) | |
775 ASSERT(top.is_constant()); | |
776 elements_[index].clear_sync(); | |
777 } | |
778 } | |
779 | |
780 | |
781 void VirtualFrame::UntaggedPushFrameSlotAt(int index) { | |
782 ASSERT(index >= 0); | |
783 ASSERT(index <= element_count()); | |
784 FrameElement original = elements_[index]; | |
785 if (original.is_copy()) { | |
786 original = elements_[original.index()]; | |
787 index = original.index(); | |
788 } | |
789 | |
790 switch (original.type()) { | |
791 case FrameElement::MEMORY: | |
792 case FrameElement::REGISTER: { | |
793 Label done; | |
794 // Emit code to load the original element's data into a register. | |
795 // Push that register as a FrameElement on top of the frame. | |
796 Result fresh = cgen()->allocator()->Allocate(); | |
797 ASSERT(fresh.is_valid()); | |
798 Register fresh_reg = fresh.reg(); | |
799 FrameElement new_element = | |
800 FrameElement::RegisterElement(fresh_reg, | |
801 FrameElement::NOT_SYNCED, | |
802 original.type_info()); | |
803 new_element.set_untagged_int32(true); | |
804 Use(fresh_reg, element_count()); | |
805 fresh.Unuse(); // BreakTarget does not handle a live Result well. | |
806 elements_.Add(new_element); | |
807 if (original.is_register()) { | |
808 __ mov(fresh_reg, original.reg()); | |
809 } else { | |
810 ASSERT(original.is_memory()); | |
811 __ mov(fresh_reg, Operand(ebp, fp_relative(index))); | |
812 } | |
813 // Now convert the value to int32, or bail out. | |
814 if (original.type_info().IsSmi()) { | |
815 __ SmiUntag(fresh_reg); | |
816 // Pushing the element is completely done. | |
817 } else { | |
818 __ test(fresh_reg, Immediate(kSmiTagMask)); | |
819 Label not_smi; | |
820 __ j(not_zero, ¬_smi); | |
821 __ SmiUntag(fresh_reg); | |
822 __ jmp(&done); | |
823 | |
824 __ bind(¬_smi); | |
825 if (!original.type_info().IsNumber()) { | |
826 __ cmp(FieldOperand(fresh_reg, HeapObject::kMapOffset), | |
827 FACTORY->heap_number_map()); | |
828 cgen()->unsafe_bailout_->Branch(not_equal); | |
829 } | |
830 | |
831 if (!CpuFeatures::IsSupported(SSE2)) { | |
832 UNREACHABLE(); | |
833 } else { | |
834 CpuFeatures::Scope use_sse2(SSE2); | |
835 __ movdbl(xmm0, FieldOperand(fresh_reg, HeapNumber::kValueOffset)); | |
836 __ cvttsd2si(fresh_reg, Operand(xmm0)); | |
837 __ cvtsi2sd(xmm1, Operand(fresh_reg)); | |
838 __ ucomisd(xmm0, xmm1); | |
839 cgen()->unsafe_bailout_->Branch(not_equal); | |
840 cgen()->unsafe_bailout_->Branch(parity_even); // NaN. | |
841 // Test for negative zero. | |
842 __ test(fresh_reg, Operand(fresh_reg)); | |
843 __ j(not_zero, &done); | |
844 __ movmskpd(fresh_reg, xmm0); | |
845 __ and_(fresh_reg, 0x1); | |
846 cgen()->unsafe_bailout_->Branch(not_equal); | |
847 } | |
848 __ bind(&done); | |
849 } | |
850 break; | |
851 } | |
852 case FrameElement::CONSTANT: | |
853 elements_.Add(CopyElementAt(index)); | |
854 elements_[element_count() - 1].set_untagged_int32(true); | |
855 break; | |
856 case FrameElement::COPY: | |
857 case FrameElement::INVALID: | |
858 UNREACHABLE(); | |
859 break; | |
860 } | |
861 } | |
862 | |
863 | |
864 void VirtualFrame::PushTryHandler(HandlerType type) { | |
865 ASSERT(cgen()->HasValidEntryRegisters()); | |
866 // Grow the expression stack by handler size less one (the return | |
867 // address is already pushed by a call instruction). | |
868 Adjust(kHandlerSize - 1); | |
869 __ PushTryHandler(IN_JAVASCRIPT, type); | |
870 } | |
871 | |
872 | |
873 Result VirtualFrame::RawCallStub(CodeStub* stub) { | |
874 ASSERT(cgen()->HasValidEntryRegisters()); | |
875 __ CallStub(stub); | |
876 Result result = cgen()->allocator()->Allocate(eax); | |
877 ASSERT(result.is_valid()); | |
878 return result; | |
879 } | |
880 | |
881 | |
882 Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) { | |
883 PrepareForCall(0, 0); | |
884 arg->ToRegister(eax); | |
885 arg->Unuse(); | |
886 return RawCallStub(stub); | |
887 } | |
888 | |
889 | |
890 Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) { | |
891 PrepareForCall(0, 0); | |
892 | |
893 if (arg0->is_register() && arg0->reg().is(eax)) { | |
894 if (arg1->is_register() && arg1->reg().is(edx)) { | |
895 // Wrong registers. | |
896 __ xchg(eax, edx); | |
897 } else { | |
898 // Register edx is free for arg0, which frees eax for arg1. | |
899 arg0->ToRegister(edx); | |
900 arg1->ToRegister(eax); | |
901 } | |
902 } else { | |
903 // Register eax is free for arg1, which guarantees edx is free for | |
904 // arg0. | |
905 arg1->ToRegister(eax); | |
906 arg0->ToRegister(edx); | |
907 } | |
908 | |
909 arg0->Unuse(); | |
910 arg1->Unuse(); | |
911 return RawCallStub(stub); | |
912 } | |
913 | |
914 | |
915 Result VirtualFrame::CallJSFunction(int arg_count) { | |
916 Result function = Pop(); | |
917 | |
918 // InvokeFunction requires function in edi. Move it in there. | |
919 function.ToRegister(edi); | |
920 function.Unuse(); | |
921 | |
922 // +1 for receiver. | |
923 PrepareForCall(arg_count + 1, arg_count + 1); | |
924 ASSERT(cgen()->HasValidEntryRegisters()); | |
925 ParameterCount count(arg_count); | |
926 __ InvokeFunction(edi, count, CALL_FUNCTION); | |
927 RestoreContextRegister(); | |
928 Result result = cgen()->allocator()->Allocate(eax); | |
929 ASSERT(result.is_valid()); | |
930 return result; | |
931 } | |
932 | |
933 | |
934 Result VirtualFrame::CallRuntime(const 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(eax); | |
939 ASSERT(result.is_valid()); | |
940 return result; | |
941 } | |
942 | |
943 | |
944 Result 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(eax); | |
949 ASSERT(result.is_valid()); | |
950 return result; | |
951 } | |
952 | |
953 | |
954 #ifdef ENABLE_DEBUGGER_SUPPORT | |
955 void VirtualFrame::DebugBreak() { | |
956 PrepareForCall(0, 0); | |
957 ASSERT(cgen()->HasValidEntryRegisters()); | |
958 __ DebugBreak(); | |
959 Result result = cgen()->allocator()->Allocate(eax); | |
960 ASSERT(result.is_valid()); | |
961 } | |
962 #endif | |
963 | |
964 | |
965 Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, | |
966 InvokeFlag flag, | |
967 int arg_count) { | |
968 PrepareForCall(arg_count, arg_count); | |
969 ASSERT(cgen()->HasValidEntryRegisters()); | |
970 __ InvokeBuiltin(id, flag); | |
971 Result result = cgen()->allocator()->Allocate(eax); | |
972 ASSERT(result.is_valid()); | |
973 return result; | |
974 } | |
975 | |
976 | |
977 Result VirtualFrame::RawCallCodeObject(Handle<Code> code, | |
978 RelocInfo::Mode rmode) { | |
979 ASSERT(cgen()->HasValidEntryRegisters()); | |
980 __ call(code, rmode); | |
981 Result result = cgen()->allocator()->Allocate(eax); | |
982 ASSERT(result.is_valid()); | |
983 return result; | |
984 } | |
985 | |
986 | |
987 // This function assumes that the only results that could be in a_reg or b_reg | |
988 // are a and b. Other results can be live, but must not be in a_reg or b_reg. | |
989 void VirtualFrame::MoveResultsToRegisters(Result* a, | |
990 Result* b, | |
991 Register a_reg, | |
992 Register b_reg) { | |
993 if (a->is_register() && a->reg().is(a_reg)) { | |
994 b->ToRegister(b_reg); | |
995 } else if (!cgen()->allocator()->is_used(a_reg)) { | |
996 a->ToRegister(a_reg); | |
997 b->ToRegister(b_reg); | |
998 } else if (cgen()->allocator()->is_used(b_reg)) { | |
999 // a must be in b_reg, b in a_reg. | |
1000 __ xchg(a_reg, b_reg); | |
1001 // Results a and b will be invalidated, so it is ok if they are switched. | |
1002 } else { | |
1003 b->ToRegister(b_reg); | |
1004 a->ToRegister(a_reg); | |
1005 } | |
1006 a->Unuse(); | |
1007 b->Unuse(); | |
1008 } | |
1009 | |
1010 | |
1011 Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) { | |
1012 // Name and receiver are on the top of the frame. The IC expects | |
1013 // name in ecx and receiver in eax. | |
1014 Result name = Pop(); | |
1015 Result receiver = Pop(); | |
1016 PrepareForCall(0, 0); // No stack arguments. | |
1017 MoveResultsToRegisters(&name, &receiver, ecx, eax); | |
1018 | |
1019 Handle<Code> ic(Isolate::Current()->builtins()->builtin( | |
1020 Builtins::kLoadIC_Initialize)); | |
1021 return RawCallCodeObject(ic, mode); | |
1022 } | |
1023 | |
1024 | |
1025 Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) { | |
1026 // Key and receiver are on top of the frame. Put them in eax and edx. | |
1027 Result key = Pop(); | |
1028 Result receiver = Pop(); | |
1029 PrepareForCall(0, 0); | |
1030 MoveResultsToRegisters(&key, &receiver, eax, edx); | |
1031 | |
1032 Handle<Code> ic(Isolate::Current()->builtins()->builtin( | |
1033 Builtins::kKeyedLoadIC_Initialize)); | |
1034 return RawCallCodeObject(ic, mode); | |
1035 } | |
1036 | |
1037 | |
1038 Result VirtualFrame::CallStoreIC(Handle<String> name, | |
1039 bool is_contextual, | |
1040 StrictModeFlag strict_mode) { | |
1041 // Value and (if not contextual) receiver are on top of the frame. | |
1042 // The IC expects name in ecx, value in eax, and receiver in edx. | |
1043 Handle<Code> ic(Isolate::Current()->builtins()->builtin( | |
1044 (strict_mode == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict | |
1045 : Builtins::kStoreIC_Initialize)); | |
1046 | |
1047 Result value = Pop(); | |
1048 RelocInfo::Mode mode; | |
1049 if (is_contextual) { | |
1050 PrepareForCall(0, 0); | |
1051 value.ToRegister(eax); | |
1052 __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); | |
1053 value.Unuse(); | |
1054 mode = RelocInfo::CODE_TARGET_CONTEXT; | |
1055 } else { | |
1056 Result receiver = Pop(); | |
1057 PrepareForCall(0, 0); | |
1058 MoveResultsToRegisters(&value, &receiver, eax, edx); | |
1059 mode = RelocInfo::CODE_TARGET; | |
1060 } | |
1061 __ mov(ecx, name); | |
1062 return RawCallCodeObject(ic, mode); | |
1063 } | |
1064 | |
1065 | |
1066 Result VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) { | |
1067 // Value, key, and receiver are on the top of the frame. The IC | |
1068 // expects value in eax, key in ecx, and receiver in edx. | |
1069 Result value = Pop(); | |
1070 Result key = Pop(); | |
1071 Result receiver = Pop(); | |
1072 PrepareForCall(0, 0); | |
1073 if (!cgen()->allocator()->is_used(eax) || | |
1074 (value.is_register() && value.reg().is(eax))) { | |
1075 if (!cgen()->allocator()->is_used(eax)) { | |
1076 value.ToRegister(eax); | |
1077 } | |
1078 MoveResultsToRegisters(&key, &receiver, ecx, edx); | |
1079 value.Unuse(); | |
1080 } else if (!cgen()->allocator()->is_used(ecx) || | |
1081 (key.is_register() && key.reg().is(ecx))) { | |
1082 if (!cgen()->allocator()->is_used(ecx)) { | |
1083 key.ToRegister(ecx); | |
1084 } | |
1085 MoveResultsToRegisters(&value, &receiver, eax, edx); | |
1086 key.Unuse(); | |
1087 } else if (!cgen()->allocator()->is_used(edx) || | |
1088 (receiver.is_register() && receiver.reg().is(edx))) { | |
1089 if (!cgen()->allocator()->is_used(edx)) { | |
1090 receiver.ToRegister(edx); | |
1091 } | |
1092 MoveResultsToRegisters(&key, &value, ecx, eax); | |
1093 receiver.Unuse(); | |
1094 } else { | |
1095 // All three registers are used, and no value is in the correct place. | |
1096 // We have one of the two circular permutations of eax, ecx, edx. | |
1097 ASSERT(value.is_register()); | |
1098 if (value.reg().is(ecx)) { | |
1099 __ xchg(eax, edx); | |
1100 __ xchg(eax, ecx); | |
1101 } else { | |
1102 __ xchg(eax, ecx); | |
1103 __ xchg(eax, edx); | |
1104 } | |
1105 value.Unuse(); | |
1106 key.Unuse(); | |
1107 receiver.Unuse(); | |
1108 } | |
1109 | |
1110 Handle<Code> ic(Isolate::Current()->builtins()->builtin( | |
1111 (strict_mode == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict | |
1112 : Builtins::kKeyedStoreIC_Initialize)); | |
1113 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); | |
1114 } | |
1115 | |
1116 | |
1117 Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, | |
1118 int arg_count, | |
1119 int loop_nesting) { | |
1120 // Function name, arguments, and receiver are on top of the frame. | |
1121 // The IC expects the name in ecx and the rest on the stack and | |
1122 // drops them all. | |
1123 InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; | |
1124 Handle<Code> ic = Isolate::Current()->stub_cache()->ComputeCallInitialize( | |
1125 arg_count, in_loop); | |
1126 // Spill args, receiver, and function. The call will drop args and | |
1127 // receiver. | |
1128 Result name = Pop(); | |
1129 PrepareForCall(arg_count + 1, arg_count + 1); // Arguments + receiver. | |
1130 name.ToRegister(ecx); | |
1131 name.Unuse(); | |
1132 return RawCallCodeObject(ic, mode); | |
1133 } | |
1134 | |
1135 | |
1136 Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode, | |
1137 int arg_count, | |
1138 int loop_nesting) { | |
1139 // Function name, arguments, and receiver are on top of the frame. | |
1140 // The IC expects the name in ecx and the rest on the stack and | |
1141 // drops them all. | |
1142 InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; | |
1143 Handle<Code> ic = | |
1144 Isolate::Current()->stub_cache()->ComputeKeyedCallInitialize(arg_count, | |
1145 in_loop); | |
1146 // Spill args, receiver, and function. The call will drop args and | |
1147 // receiver. | |
1148 Result name = Pop(); | |
1149 PrepareForCall(arg_count + 1, arg_count + 1); // Arguments + receiver. | |
1150 name.ToRegister(ecx); | |
1151 name.Unuse(); | |
1152 return RawCallCodeObject(ic, mode); | |
1153 } | |
1154 | |
1155 | |
1156 Result VirtualFrame::CallConstructor(int arg_count) { | |
1157 // Arguments, receiver, and function are on top of the frame. The | |
1158 // IC expects arg count in eax, function in edi, and the arguments | |
1159 // and receiver on the stack. | |
1160 Handle<Code> ic(Isolate::Current()->builtins()->builtin( | |
1161 Builtins::kJSConstructCall)); | |
1162 // Duplicate the function before preparing the frame. | |
1163 PushElementAt(arg_count); | |
1164 Result function = Pop(); | |
1165 PrepareForCall(arg_count + 1, arg_count + 1); // Spill function and args. | |
1166 function.ToRegister(edi); | |
1167 | |
1168 // Constructors are called with the number of arguments in register | |
1169 // eax for now. Another option would be to have separate construct | |
1170 // call trampolines per different arguments counts encountered. | |
1171 Result num_args = cgen()->allocator()->Allocate(eax); | |
1172 ASSERT(num_args.is_valid()); | |
1173 __ Set(num_args.reg(), Immediate(arg_count)); | |
1174 | |
1175 function.Unuse(); | |
1176 num_args.Unuse(); | |
1177 return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL); | |
1178 } | |
1179 | |
1180 | |
1181 void VirtualFrame::Drop(int count) { | |
1182 ASSERT(count >= 0); | |
1183 ASSERT(height() >= count); | |
1184 int num_virtual_elements = (element_count() - 1) - stack_pointer_; | |
1185 | |
1186 // Emit code to lower the stack pointer if necessary. | |
1187 if (num_virtual_elements < count) { | |
1188 int num_dropped = count - num_virtual_elements; | |
1189 stack_pointer_ -= num_dropped; | |
1190 __ add(Operand(esp), Immediate(num_dropped * kPointerSize)); | |
1191 } | |
1192 | |
1193 // Discard elements from the virtual frame and free any registers. | |
1194 for (int i = 0; i < count; i++) { | |
1195 FrameElement dropped = elements_.RemoveLast(); | |
1196 if (dropped.is_register()) { | |
1197 Unuse(dropped.reg()); | |
1198 } | |
1199 } | |
1200 } | |
1201 | |
1202 | |
1203 Result VirtualFrame::Pop() { | |
1204 FrameElement element = elements_.RemoveLast(); | |
1205 int index = element_count(); | |
1206 ASSERT(element.is_valid()); | |
1207 ASSERT(element.is_untagged_int32() == cgen()->in_safe_int32_mode()); | |
1208 | |
1209 // Get number type information of the result. | |
1210 TypeInfo info; | |
1211 if (!element.is_copy()) { | |
1212 info = element.type_info(); | |
1213 } else { | |
1214 info = elements_[element.index()].type_info(); | |
1215 } | |
1216 | |
1217 bool pop_needed = (stack_pointer_ == index); | |
1218 if (pop_needed) { | |
1219 stack_pointer_--; | |
1220 if (element.is_memory()) { | |
1221 Result temp = cgen()->allocator()->Allocate(); | |
1222 ASSERT(temp.is_valid()); | |
1223 __ pop(temp.reg()); | |
1224 temp.set_type_info(info); | |
1225 temp.set_untagged_int32(element.is_untagged_int32()); | |
1226 return temp; | |
1227 } | |
1228 | |
1229 __ add(Operand(esp), Immediate(kPointerSize)); | |
1230 } | |
1231 ASSERT(!element.is_memory()); | |
1232 | |
1233 // The top element is a register, constant, or a copy. Unuse | |
1234 // registers and follow copies to their backing store. | |
1235 if (element.is_register()) { | |
1236 Unuse(element.reg()); | |
1237 } else if (element.is_copy()) { | |
1238 ASSERT(!element.is_untagged_int32()); | |
1239 ASSERT(element.index() < index); | |
1240 index = element.index(); | |
1241 element = elements_[index]; | |
1242 } | |
1243 ASSERT(!element.is_copy()); | |
1244 | |
1245 // The element is memory, a register, or a constant. | |
1246 if (element.is_memory()) { | |
1247 // Memory elements could only be the backing store of a copy. | |
1248 // Allocate the original to a register. | |
1249 ASSERT(index <= stack_pointer_); | |
1250 ASSERT(!element.is_untagged_int32()); | |
1251 Result temp = cgen()->allocator()->Allocate(); | |
1252 ASSERT(temp.is_valid()); | |
1253 Use(temp.reg(), index); | |
1254 FrameElement new_element = | |
1255 FrameElement::RegisterElement(temp.reg(), | |
1256 FrameElement::SYNCED, | |
1257 element.type_info()); | |
1258 // Preserve the copy flag on the element. | |
1259 if (element.is_copied()) new_element.set_copied(); | |
1260 elements_[index] = new_element; | |
1261 __ mov(temp.reg(), Operand(ebp, fp_relative(index))); | |
1262 return Result(temp.reg(), info); | |
1263 } else if (element.is_register()) { | |
1264 Result return_value(element.reg(), info); | |
1265 return_value.set_untagged_int32(element.is_untagged_int32()); | |
1266 return return_value; | |
1267 } else { | |
1268 ASSERT(element.is_constant()); | |
1269 Result return_value(element.handle()); | |
1270 return_value.set_untagged_int32(element.is_untagged_int32()); | |
1271 return return_value; | |
1272 } | |
1273 } | |
1274 | |
1275 | |
1276 void VirtualFrame::EmitPop(Register reg) { | |
1277 ASSERT(stack_pointer_ == element_count() - 1); | |
1278 stack_pointer_--; | |
1279 elements_.RemoveLast(); | |
1280 __ pop(reg); | |
1281 } | |
1282 | |
1283 | |
1284 void VirtualFrame::EmitPop(Operand operand) { | |
1285 ASSERT(stack_pointer_ == element_count() - 1); | |
1286 stack_pointer_--; | |
1287 elements_.RemoveLast(); | |
1288 __ pop(operand); | |
1289 } | |
1290 | |
1291 | |
1292 void VirtualFrame::EmitPush(Register reg, TypeInfo info) { | |
1293 ASSERT(stack_pointer_ == element_count() - 1); | |
1294 elements_.Add(FrameElement::MemoryElement(info)); | |
1295 stack_pointer_++; | |
1296 __ push(reg); | |
1297 } | |
1298 | |
1299 | |
1300 void VirtualFrame::EmitPush(Operand operand, TypeInfo info) { | |
1301 ASSERT(stack_pointer_ == element_count() - 1); | |
1302 elements_.Add(FrameElement::MemoryElement(info)); | |
1303 stack_pointer_++; | |
1304 __ push(operand); | |
1305 } | |
1306 | |
1307 | |
1308 void VirtualFrame::EmitPush(Immediate immediate, TypeInfo info) { | |
1309 ASSERT(stack_pointer_ == element_count() - 1); | |
1310 elements_.Add(FrameElement::MemoryElement(info)); | |
1311 stack_pointer_++; | |
1312 __ push(immediate); | |
1313 } | |
1314 | |
1315 | |
1316 void VirtualFrame::PushUntaggedElement(Handle<Object> value) { | |
1317 ASSERT(!ConstantPoolOverflowed()); | |
1318 elements_.Add(FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED)); | |
1319 elements_[element_count() - 1].set_untagged_int32(true); | |
1320 } | |
1321 | |
1322 | |
1323 void VirtualFrame::Push(Expression* expr) { | |
1324 ASSERT(expr->IsTrivial()); | |
1325 | |
1326 Literal* lit = expr->AsLiteral(); | |
1327 if (lit != NULL) { | |
1328 Push(lit->handle()); | |
1329 return; | |
1330 } | |
1331 | |
1332 VariableProxy* proxy = expr->AsVariableProxy(); | |
1333 if (proxy != NULL) { | |
1334 Slot* slot = proxy->var()->AsSlot(); | |
1335 if (slot->type() == Slot::LOCAL) { | |
1336 PushLocalAt(slot->index()); | |
1337 return; | |
1338 } | |
1339 if (slot->type() == Slot::PARAMETER) { | |
1340 PushParameterAt(slot->index()); | |
1341 return; | |
1342 } | |
1343 } | |
1344 UNREACHABLE(); | |
1345 } | |
1346 | |
1347 | |
1348 void VirtualFrame::Push(Handle<Object> value) { | |
1349 if (ConstantPoolOverflowed()) { | |
1350 Result temp = cgen()->allocator()->Allocate(); | |
1351 ASSERT(temp.is_valid()); | |
1352 __ Set(temp.reg(), Immediate(value)); | |
1353 Push(&temp); | |
1354 } else { | |
1355 FrameElement element = | |
1356 FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED); | |
1357 elements_.Add(element); | |
1358 } | |
1359 } | |
1360 | |
1361 | |
1362 #undef __ | |
1363 | |
1364 } } // namespace v8::internal | |
1365 | |
1366 #endif // V8_TARGET_ARCH_IA32 | |
OLD | NEW |