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 #include "codegen-inl.h" | |
31 #include "register-allocator-inl.h" | |
32 #include "scopes.h" | |
33 | |
34 namespace v8 { namespace internal { | |
35 | |
36 #define __ ACCESS_MASM(masm_) | |
37 | |
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. | |
43 VirtualFrame::VirtualFrame(CodeGenerator* cgen) | |
44 : cgen_(cgen), | |
45 masm_(cgen->masm()), | |
46 elements_(cgen->scope()->num_parameters() | |
47 + cgen->scope()->num_stack_slots() | |
48 + kPreallocatedElements), | |
49 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 } | |
56 for (int i = 0; i < kNumRegisters; i++) { | |
57 register_locations_[i] = kIllegalIndex; | |
58 } | |
59 } | |
60 | |
61 | |
62 void 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_); | |
66 FrameElement element = elements_[index]; | |
67 ASSERT(!element.is_synced()); | |
68 switch (element.type()) { | |
69 case FrameElement::INVALID: | |
70 break; | |
71 | |
72 case FrameElement::MEMORY: | |
73 // This function should not be called with synced elements. | |
74 // (memory elements are always synced). | |
75 UNREACHABLE(); | |
76 break; | |
77 | |
78 case FrameElement::REGISTER: | |
79 __ mov(Operand(ebp, fp_relative(index)), element.reg()); | |
80 break; | |
81 | |
82 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())); | |
91 } | |
92 break; | |
93 | |
94 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()); | |
105 } | |
106 break; | |
107 } | |
108 } | |
109 elements_[index].set_sync(); | |
110 } | |
111 | |
112 | |
113 void 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 } | |
157 elements_[index].set_sync(); | |
158 } | |
159 | |
160 | |
161 void VirtualFrame::MergeTo(VirtualFrame* expected) { | |
162 Comment cmnt(masm_, "[ Merge frame"); | |
163 // We should always be merging the code generator's current frame to an | |
164 // expected frame. | |
165 ASSERT(cgen_->frame() == this); | |
166 | |
167 // Adjust the stack pointer upward (toward the top of the virtual | |
168 // frame) if necessary. | |
169 if (stack_pointer_ < expected->stack_pointer_) { | |
170 int difference = expected->stack_pointer_ - stack_pointer_; | |
171 stack_pointer_ = expected->stack_pointer_; | |
172 __ sub(Operand(esp), Immediate(difference * kPointerSize)); | |
173 } | |
174 | |
175 MergeMoveRegistersToMemory(expected); | |
176 MergeMoveRegistersToRegisters(expected); | |
177 MergeMoveMemoryToRegisters(expected); | |
178 | |
179 // Fix any sync flag problems from the bottom-up and make the copied | |
180 // flags exact. This assumes that the backing store of copies is | |
181 // always lower in the frame. | |
182 for (int i = 0; i < elements_.length(); i++) { | |
183 FrameElement source = elements_[i]; | |
184 FrameElement target = expected->elements_[i]; | |
185 if (source.is_synced() && !target.is_synced()) { | |
186 elements_[i].clear_sync(); | |
187 } else if (!source.is_synced() && target.is_synced()) { | |
188 SyncElementAt(i); | |
189 } | |
190 elements_[i].clear_copied(); | |
191 if (elements_[i].is_copy()) { | |
192 elements_[elements_[i].index()].set_copied(); | |
193 } | |
194 } | |
195 | |
196 // Adjust the stack pointer downward if necessary. | |
197 if (stack_pointer_ > expected->stack_pointer_) { | |
198 int difference = stack_pointer_ - expected->stack_pointer_; | |
199 stack_pointer_ = expected->stack_pointer_; | |
200 __ add(Operand(esp), Immediate(difference * kPointerSize)); | |
201 } | |
202 | |
203 // At this point, the frames should be identical. | |
204 ASSERT(Equals(expected)); | |
205 } | |
206 | |
207 | |
208 void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { | |
209 ASSERT(stack_pointer_ >= expected->stack_pointer_); | |
210 | |
211 // Move registers, constants, and copies to memory. Perform moves | |
212 // from the top downward in the frame in order to leave the backing | |
213 // stores of copies in registers. | |
214 // | |
215 // Moving memory-backed copies to memory requires a spare register | |
216 // for the memory-to-memory moves. Since we are performing a merge, | |
217 // we use esi (which is already saved in the frame). We keep track | |
218 // of the index of the frame element esi is caching or kIllegalIndex | |
219 // if esi has not been disturbed. | |
220 int esi_caches = kIllegalIndex; | |
221 // A "singleton" memory element. | |
222 FrameElement memory_element = FrameElement::MemoryElement(); | |
223 // Loop downward from the stack pointer or the top of the frame if | |
224 // the stack pointer is floating above the frame. | |
225 int start = Min(stack_pointer_, elements_.length() - 1); | |
226 for (int i = start; i >= 0; i--) { | |
227 FrameElement target = expected->elements_[i]; | |
228 if (target.is_memory()) { | |
229 FrameElement source = elements_[i]; | |
230 switch (source.type()) { | |
231 case FrameElement::INVALID: | |
232 // Not a legal merge move. | |
233 UNREACHABLE(); | |
234 break; | |
235 | |
236 case FrameElement::MEMORY: | |
237 // Already in place. | |
238 break; | |
239 | |
240 case FrameElement::REGISTER: | |
241 Unuse(source.reg()); | |
242 if (!source.is_synced()) { | |
243 __ mov(Operand(ebp, fp_relative(i)), source.reg()); | |
244 } | |
245 break; | |
246 | |
247 case FrameElement::CONSTANT: | |
248 if (!source.is_synced()) { | |
249 if (cgen_->IsUnsafeSmi(source.handle())) { | |
250 esi_caches = i; | |
251 cgen_->LoadUnsafeSmi(esi, source.handle()); | |
252 __ mov(Operand(ebp, fp_relative(i)), esi); | |
253 } else { | |
254 __ Set(Operand(ebp, fp_relative(i)), Immediate(source.handle())); | |
255 } | |
256 } | |
257 break; | |
258 | |
259 case FrameElement::COPY: | |
260 if (!source.is_synced()) { | |
261 int backing_index = source.index(); | |
262 FrameElement backing_element = elements_[backing_index]; | |
263 if (backing_element.is_memory()) { | |
264 // If we have to spill a register, we spill esi. | |
265 if (esi_caches != backing_index) { | |
266 esi_caches = backing_index; | |
267 __ mov(esi, Operand(ebp, fp_relative(backing_index))); | |
268 } | |
269 __ mov(Operand(ebp, fp_relative(i)), esi); | |
270 } else { | |
271 ASSERT(backing_element.is_register()); | |
272 __ mov(Operand(ebp, fp_relative(i)), backing_element.reg()); | |
273 } | |
274 } | |
275 break; | |
276 } | |
277 elements_[i] = memory_element; | |
278 } | |
279 } | |
280 | |
281 if (esi_caches != kIllegalIndex) { | |
282 __ mov(esi, Operand(ebp, fp_relative(context_index()))); | |
283 } | |
284 } | |
285 | |
286 | |
287 void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) { | |
288 // We have already done X-to-memory moves. | |
289 ASSERT(stack_pointer_ >= expected->stack_pointer_); | |
290 | |
291 // Perform register-to-register moves. | |
292 int start = 0; | |
293 int end = elements_.length() - 1; | |
294 bool any_moves_blocked; // Did we fail to make some moves this iteration? | |
295 bool should_break_cycles = false; | |
296 bool any_moves_made; // Did we make any progress this iteration? | |
297 do { | |
298 any_moves_blocked = false; | |
299 any_moves_made = false; | |
300 int first_move_blocked = kIllegalIndex; | |
301 int last_move_blocked = kIllegalIndex; | |
302 for (int i = start; i <= end; i++) { | |
303 FrameElement source = elements_[i]; | |
304 FrameElement target = expected->elements_[i]; | |
305 if (source.is_register() && target.is_register()) { | |
306 if (target.reg().is(source.reg())) { | |
307 if (target.is_synced() && !source.is_synced()) { | |
308 __ mov(Operand(ebp, fp_relative(i)), source.reg()); | |
309 } | |
310 elements_[i] = target; | |
311 } else { | |
312 // We need to move source to target. | |
313 if (is_used(target.reg())) { | |
314 // The move is blocked because the target contains valid data. | |
315 // If we are stuck with only cycles remaining, then we spill source. | |
316 // Otherwise, we just need more iterations. | |
317 if (should_break_cycles) { | |
318 SpillElementAt(i); | |
319 should_break_cycles = false; | |
320 } else { // Record a blocked move. | |
321 if (!any_moves_blocked) { | |
322 first_move_blocked = i; | |
323 } | |
324 last_move_blocked = i; | |
325 any_moves_blocked = true; | |
326 } | |
327 } else { | |
328 // The move is not blocked. This frame element can be moved from | |
329 // its source register to its target register. | |
330 if (target.is_synced() && !source.is_synced()) { | |
331 SyncElementAt(i); | |
332 } | |
333 Use(target.reg(), i); | |
334 Unuse(source.reg()); | |
335 elements_[i] = target; | |
336 __ mov(target.reg(), source.reg()); | |
337 any_moves_made = true; | |
338 } | |
339 } | |
340 } | |
341 } | |
342 // Update control flags for next iteration. | |
343 should_break_cycles = (any_moves_blocked && !any_moves_made); | |
344 if (any_moves_blocked) { | |
345 start = first_move_blocked; | |
346 end = last_move_blocked; | |
347 } | |
348 } while (any_moves_blocked); | |
349 } | |
350 | |
351 | |
352 void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) { | |
353 // Move memory, constants, and copies to registers. This is the | |
354 // final step and is done from the bottom up so that the backing | |
355 // elements of copies are in their correct locations when we | |
356 // encounter the copies. | |
357 for (int i = 0; i < elements_.length(); i++) { | |
358 FrameElement source = elements_[i]; | |
359 FrameElement target = expected->elements_[i]; | |
360 if (target.is_register() && !source.is_register()) { | |
361 switch (source.type()) { | |
362 case FrameElement::INVALID: // Fall through. | |
363 case FrameElement::REGISTER: | |
364 UNREACHABLE(); | |
365 break; | |
366 | |
367 case FrameElement::MEMORY: | |
368 ASSERT(i <= stack_pointer_); | |
369 __ mov(target.reg(), Operand(ebp, fp_relative(i))); | |
370 break; | |
371 | |
372 case FrameElement::CONSTANT: | |
373 if (cgen_->IsUnsafeSmi(source.handle())) { | |
374 cgen_->LoadUnsafeSmi(target.reg(), source.handle()); | |
375 } else { | |
376 __ Set(target.reg(), Immediate(source.handle())); | |
377 } | |
378 break; | |
379 | |
380 case FrameElement::COPY: { | |
381 FrameElement backing = elements_[source.index()]; | |
382 ASSERT(backing.is_memory() || backing.is_register()); | |
383 if (backing.is_memory()) { | |
384 ASSERT(source.index() <= stack_pointer_); | |
385 __ mov(target.reg(), Operand(ebp, fp_relative(source.index()))); | |
386 } else { | |
387 __ mov(target.reg(), backing.reg()); | |
388 } | |
389 } | |
390 } | |
391 // Ensure the proper sync state. If the source was memory no | |
392 // code needs to be emitted. | |
393 if (target.is_synced() && !source.is_memory()) { | |
394 SyncElementAt(i); | |
395 } | |
396 Use(target.reg(), i); | |
397 elements_[i] = target; | |
398 } | |
399 } | |
400 } | |
401 | |
402 | |
403 void VirtualFrame::Enter() { | |
404 // Registers live on entry: esp, ebp, esi, edi. | |
405 Comment cmnt(masm_, "[ Enter JS frame"); | |
406 | |
407 #ifdef DEBUG | |
408 // Verify that edi contains a JS function. The following code | |
409 // relies on eax being available for use. | |
410 __ test(edi, Immediate(kSmiTagMask)); | |
411 __ Check(not_zero, | |
412 "VirtualFrame::Enter - edi is not a function (smi check)."); | |
413 __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); | |
414 __ Check(equal, | |
415 "VirtualFrame::Enter - edi is not a function (map check)."); | |
416 #endif | |
417 | |
418 EmitPush(ebp); | |
419 | |
420 frame_pointer_ = stack_pointer_; | |
421 __ mov(ebp, Operand(esp)); | |
422 | |
423 // Store the context in the frame. The context is kept in esi and a | |
424 // copy is stored in the frame. The external reference to esi | |
425 // remains. | |
426 EmitPush(esi); | |
427 | |
428 // Store the function in the frame. The frame owns the register | |
429 // reference now (ie, it can keep it in edi or spill it later). | |
430 Push(edi); | |
431 SyncElementAt(elements_.length() - 1); | |
432 cgen_->allocator()->Unuse(edi); | |
433 } | |
434 | |
435 | |
436 void VirtualFrame::Exit() { | |
437 Comment cmnt(masm_, "[ Exit JS frame"); | |
438 // Record the location of the JS exit code for patching when setting | |
439 // break point. | |
440 __ RecordJSReturn(); | |
441 | |
442 // Avoid using the leave instruction here, because it is too | |
443 // short. We need the return sequence to be a least the size of a | |
444 // call instruction to support patching the exit code in the | |
445 // debugger. See VisitReturnStatement for the full return sequence. | |
446 __ mov(esp, Operand(ebp)); | |
447 stack_pointer_ = frame_pointer_; | |
448 for (int i = elements_.length() - 1; i > stack_pointer_; i--) { | |
449 FrameElement last = elements_.RemoveLast(); | |
450 if (last.is_register()) { | |
451 Unuse(last.reg()); | |
452 } | |
453 } | |
454 | |
455 frame_pointer_ = kIllegalIndex; | |
456 EmitPop(ebp); | |
457 } | |
458 | |
459 | |
460 void VirtualFrame::AllocateStackSlots(int count) { | |
461 ASSERT(height() == 0); | |
462 local_count_ = count; | |
463 | |
464 if (count > 0) { | |
465 Comment cmnt(masm_, "[ Allocate space for locals"); | |
466 // The locals are initialized to a constant (the undefined value), but | |
467 // we sync them with the actual frame to allocate space for spilling | |
468 // them later. First sync everything above the stack pointer so we can | |
469 // use pushes to allocate and initialize the locals. | |
470 SyncRange(stack_pointer_ + 1, elements_.length()); | |
471 Handle<Object> undefined = Factory::undefined_value(); | |
472 FrameElement initial_value = | |
473 FrameElement::ConstantElement(undefined, FrameElement::SYNCED); | |
474 Result temp = cgen_->allocator()->Allocate(); | |
475 ASSERT(temp.is_valid()); | |
476 __ Set(temp.reg(), Immediate(undefined)); | |
477 for (int i = 0; i < count; i++) { | |
478 elements_.Add(initial_value); | |
479 stack_pointer_++; | |
480 __ push(temp.reg()); | |
481 } | |
482 } | |
483 } | |
484 | |
485 | |
486 void VirtualFrame::SaveContextRegister() { | |
487 ASSERT(elements_[context_index()].is_memory()); | |
488 __ mov(Operand(ebp, fp_relative(context_index())), esi); | |
489 } | |
490 | |
491 | |
492 void VirtualFrame::RestoreContextRegister() { | |
493 ASSERT(elements_[context_index()].is_memory()); | |
494 __ mov(esi, Operand(ebp, fp_relative(context_index()))); | |
495 } | |
496 | |
497 | |
498 void VirtualFrame::PushReceiverSlotAddress() { | |
499 Result temp = cgen_->allocator()->Allocate(); | |
500 ASSERT(temp.is_valid()); | |
501 __ lea(temp.reg(), ParameterAt(-1)); | |
502 Push(&temp); | |
503 } | |
504 | |
505 | |
506 int VirtualFrame::InvalidateFrameSlotAt(int index) { | |
507 FrameElement original = elements_[index]; | |
508 | |
509 // Is this element the backing store of any copies? | |
510 int new_backing_index = kIllegalIndex; | |
511 if (original.is_copied()) { | |
512 // Verify it is copied, and find first copy. | |
513 for (int i = index + 1; i < elements_.length(); i++) { | |
514 if (elements_[i].is_copy() && elements_[i].index() == index) { | |
515 new_backing_index = i; | |
516 break; | |
517 } | |
518 } | |
519 } | |
520 | |
521 if (new_backing_index == kIllegalIndex) { | |
522 // No copies found, return kIllegalIndex. | |
523 if (original.is_register()) { | |
524 Unuse(original.reg()); | |
525 } | |
526 elements_[index] = FrameElement::InvalidElement(); | |
527 return kIllegalIndex; | |
528 } | |
529 | |
530 // This is the backing store of copies. | |
531 Register backing_reg; | |
532 if (original.is_memory()) { | |
533 Result fresh = cgen_->allocator()->Allocate(); | |
534 ASSERT(fresh.is_valid()); | |
535 Use(fresh.reg(), new_backing_index); | |
536 backing_reg = fresh.reg(); | |
537 __ mov(backing_reg, Operand(ebp, fp_relative(index))); | |
538 } else { | |
539 // The original was in a register. | |
540 backing_reg = original.reg(); | |
541 register_locations_[backing_reg.code()] = new_backing_index; | |
542 } | |
543 // Invalidate the element at index. | |
544 elements_[index] = FrameElement::InvalidElement(); | |
545 // Set the new backing element. | |
546 if (elements_[new_backing_index].is_synced()) { | |
547 elements_[new_backing_index] = | |
548 FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED); | |
549 } else { | |
550 elements_[new_backing_index] = | |
551 FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED); | |
552 } | |
553 // Update the other copies. | |
554 for (int i = new_backing_index + 1; i < elements_.length(); i++) { | |
555 if (elements_[i].is_copy() && elements_[i].index() == index) { | |
556 elements_[i].set_index(new_backing_index); | |
557 elements_[new_backing_index].set_copied(); | |
558 } | |
559 } | |
560 return new_backing_index; | |
561 } | |
562 | |
563 | |
564 void VirtualFrame::TakeFrameSlotAt(int index) { | |
565 ASSERT(index >= 0); | |
566 ASSERT(index <= elements_.length()); | |
567 FrameElement original = elements_[index]; | |
568 int new_backing_store_index = InvalidateFrameSlotAt(index); | |
569 if (new_backing_store_index != kIllegalIndex) { | |
570 elements_.Add(CopyElementAt(new_backing_store_index)); | |
571 return; | |
572 } | |
573 | |
574 switch (original.type()) { | |
575 case FrameElement::MEMORY: { | |
576 // Emit code to load the original element's data into a register. | |
577 // Push that register as a FrameElement on top of the frame. | |
578 Result fresh = cgen_->allocator()->Allocate(); | |
579 ASSERT(fresh.is_valid()); | |
580 FrameElement new_element = | |
581 FrameElement::RegisterElement(fresh.reg(), | |
582 FrameElement::NOT_SYNCED); | |
583 Use(fresh.reg(), elements_.length()); | |
584 elements_.Add(new_element); | |
585 __ mov(fresh.reg(), Operand(ebp, fp_relative(index))); | |
586 break; | |
587 } | |
588 case FrameElement::REGISTER: | |
589 Use(original.reg(), elements_.length()); | |
590 // Fall through. | |
591 case FrameElement::CONSTANT: | |
592 case FrameElement::COPY: | |
593 original.clear_sync(); | |
594 elements_.Add(original); | |
595 break; | |
596 case FrameElement::INVALID: | |
597 UNREACHABLE(); | |
598 break; | |
599 } | |
600 } | |
601 | |
602 | |
603 void VirtualFrame::StoreToFrameSlotAt(int index) { | |
604 // Store the value on top of the frame to the virtual frame slot at | |
605 // a given index. The value on top of the frame is left in place. | |
606 // This is a duplicating operation, so it can create copies. | |
607 ASSERT(index >= 0); | |
608 ASSERT(index < elements_.length()); | |
609 | |
610 int top_index = elements_.length() - 1; | |
611 FrameElement top = elements_[top_index]; | |
612 FrameElement original = elements_[index]; | |
613 if (top.is_copy() && top.index() == index) return; | |
614 ASSERT(top.is_valid()); | |
615 | |
616 InvalidateFrameSlotAt(index); | |
617 | |
618 if (top.is_copy()) { | |
619 // There are two cases based on the relative positions of the | |
620 // stored-to slot and the backing slot of the top element. | |
621 int backing_index = top.index(); | |
622 ASSERT(backing_index != index); | |
623 if (backing_index < index) { | |
624 // 1. The top element is a copy of a slot below the stored-to | |
625 // slot. The stored-to slot becomes an unsynced copy of that | |
626 // same backing slot. | |
627 elements_[index] = CopyElementAt(backing_index); | |
628 } else { | |
629 // 2. The top element is a copy of a slot above the stored-to | |
630 // slot. The stored-to slot becomes the new (unsynced) backing | |
631 // slot and both the top element and the element at the former | |
632 // backing slot become copies of it. The sync state of the top | |
633 // and former backing elements is preserved. | |
634 FrameElement backing_element = elements_[backing_index]; | |
635 ASSERT(backing_element.is_memory() || backing_element.is_register()); | |
636 if (backing_element.is_memory()) { | |
637 // Because sets of copies are canonicalized to be backed by | |
638 // their lowest frame element, and because memory frame | |
639 // elements are backed by the corresponding stack address, we | |
640 // have to move the actual value down in the stack. | |
641 // | |
642 // TODO(209): considering allocating the stored-to slot to the | |
643 // temp register. Alternatively, allow copies to appear in | |
644 // any order in the frame and lazily move the value down to | |
645 // the slot. | |
646 Result temp = cgen_->allocator()->Allocate(); | |
647 ASSERT(temp.is_valid()); | |
648 __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index))); | |
649 __ mov(Operand(ebp, fp_relative(index)), temp.reg()); | |
650 } else { | |
651 register_locations_[backing_element.reg().code()] = index; | |
652 if (backing_element.is_synced()) { | |
653 // If the element is a register, we will not actually move | |
654 // anything on the stack but only update the virtual frame | |
655 // element. | |
656 backing_element.clear_sync(); | |
657 } | |
658 } | |
659 elements_[index] = backing_element; | |
660 | |
661 // The old backing element becomes a copy of the new backing | |
662 // element. | |
663 FrameElement new_element = CopyElementAt(index); | |
664 elements_[backing_index] = new_element; | |
665 if (backing_element.is_synced()) { | |
666 elements_[backing_index].set_sync(); | |
667 } | |
668 | |
669 // All the copies of the old backing element (including the top | |
670 // element) become copies of the new backing element. | |
671 for (int i = backing_index + 1; i < elements_.length(); i++) { | |
672 if (elements_[i].is_copy() && elements_[i].index() == backing_index) { | |
673 elements_[i].set_index(index); | |
674 } | |
675 } | |
676 } | |
677 return; | |
678 } | |
679 | |
680 // Move the top element to the stored-to slot and replace it (the | |
681 // top element) with a copy. | |
682 elements_[index] = top; | |
683 if (top.is_memory()) { | |
684 // TODO(209): consider allocating the stored-to slot to the temp | |
685 // register. Alternatively, allow copies to appear in any order | |
686 // in the frame and lazily move the value down to the slot. | |
687 FrameElement new_top = CopyElementAt(index); | |
688 new_top.set_sync(); | |
689 elements_[top_index] = new_top; | |
690 | |
691 // The sync state of the former top element is correct (synced). | |
692 // Emit code to move the value down in the frame. | |
693 Result temp = cgen_->allocator()->Allocate(); | |
694 ASSERT(temp.is_valid()); | |
695 __ mov(temp.reg(), Operand(esp, 0)); | |
696 __ mov(Operand(ebp, fp_relative(index)), temp.reg()); | |
697 } else if (top.is_register()) { | |
698 register_locations_[top.reg().code()] = index; | |
699 // The stored-to slot has the (unsynced) register reference and | |
700 // the top element becomes a copy. The sync state of the top is | |
701 // preserved. | |
702 FrameElement new_top = CopyElementAt(index); | |
703 if (top.is_synced()) { | |
704 new_top.set_sync(); | |
705 elements_[index].clear_sync(); | |
706 } | |
707 elements_[top_index] = new_top; | |
708 } else { | |
709 // The stored-to slot holds the same value as the top but | |
710 // unsynced. (We do not have copies of constants yet.) | |
711 ASSERT(top.is_constant()); | |
712 elements_[index].clear_sync(); | |
713 } | |
714 } | |
715 | |
716 | |
717 void VirtualFrame::PushTryHandler(HandlerType type) { | |
718 ASSERT(cgen_->HasValidEntryRegisters()); | |
719 // Grow the expression stack by handler size less two (the return address | |
720 // is already pushed by a call instruction, and PushTryHandler from the | |
721 // macro assembler will leave the top of stack in the eax register to be | |
722 // pushed separately). | |
723 Adjust(kHandlerSize - 2); | |
724 __ PushTryHandler(IN_JAVASCRIPT, type); | |
725 // TODO(1222589): remove the reliance of PushTryHandler on a cached TOS | |
726 EmitPush(eax); | |
727 } | |
728 | |
729 | |
730 Result VirtualFrame::RawCallStub(CodeStub* stub) { | |
731 ASSERT(cgen_->HasValidEntryRegisters()); | |
732 __ CallStub(stub); | |
733 Result result = cgen_->allocator()->Allocate(eax); | |
734 ASSERT(result.is_valid()); | |
735 return result; | |
736 } | |
737 | |
738 | |
739 Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) { | |
740 PrepareForCall(0, 0); | |
741 arg->ToRegister(eax); | |
742 arg->Unuse(); | |
743 return RawCallStub(stub); | |
744 } | |
745 | |
746 | |
747 Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) { | |
748 PrepareForCall(0, 0); | |
749 | |
750 if (arg0->is_register() && arg0->reg().is(eax)) { | |
751 if (arg1->is_register() && arg1->reg().is(edx)) { | |
752 // Wrong registers. | |
753 __ xchg(eax, edx); | |
754 } else { | |
755 // Register edx is free for arg0, which frees eax for arg1. | |
756 arg0->ToRegister(edx); | |
757 arg1->ToRegister(eax); | |
758 } | |
759 } else { | |
760 // Register eax is free for arg1, which guarantees edx is free for | |
761 // arg0. | |
762 arg1->ToRegister(eax); | |
763 arg0->ToRegister(edx); | |
764 } | |
765 | |
766 arg0->Unuse(); | |
767 arg1->Unuse(); | |
768 return RawCallStub(stub); | |
769 } | |
770 | |
771 | |
772 Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { | |
773 PrepareForCall(arg_count, arg_count); | |
774 ASSERT(cgen_->HasValidEntryRegisters()); | |
775 __ CallRuntime(f, arg_count); | |
776 Result result = cgen_->allocator()->Allocate(eax); | |
777 ASSERT(result.is_valid()); | |
778 return result; | |
779 } | |
780 | |
781 | |
782 Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { | |
783 PrepareForCall(arg_count, arg_count); | |
784 ASSERT(cgen_->HasValidEntryRegisters()); | |
785 __ CallRuntime(id, arg_count); | |
786 Result result = cgen_->allocator()->Allocate(eax); | |
787 ASSERT(result.is_valid()); | |
788 return result; | |
789 } | |
790 | |
791 | |
792 Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, | |
793 InvokeFlag flag, | |
794 int arg_count) { | |
795 PrepareForCall(arg_count, arg_count); | |
796 ASSERT(cgen_->HasValidEntryRegisters()); | |
797 __ InvokeBuiltin(id, flag); | |
798 Result result = cgen_->allocator()->Allocate(eax); | |
799 ASSERT(result.is_valid()); | |
800 return result; | |
801 } | |
802 | |
803 | |
804 Result VirtualFrame::RawCallCodeObject(Handle<Code> code, | |
805 RelocInfo::Mode rmode) { | |
806 ASSERT(cgen_->HasValidEntryRegisters()); | |
807 __ call(code, rmode); | |
808 Result result = cgen_->allocator()->Allocate(eax); | |
809 ASSERT(result.is_valid()); | |
810 return result; | |
811 } | |
812 | |
813 | |
814 Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) { | |
815 // Name and receiver are on the top of the frame. The IC expects | |
816 // name in ecx and receiver on the stack. It does not drop the | |
817 // receiver. | |
818 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | |
819 Result name = Pop(); | |
820 PrepareForCall(1, 0); // One stack arg, not callee-dropped. | |
821 name.ToRegister(ecx); | |
822 name.Unuse(); | |
823 return RawCallCodeObject(ic, mode); | |
824 } | |
825 | |
826 | |
827 Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) { | |
828 // Key and receiver are on top of the frame. The IC expects them on | |
829 // the stack. It does not drop them. | |
830 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
831 PrepareForCall(2, 0); // Two stack args, neither callee-dropped. | |
832 return RawCallCodeObject(ic, mode); | |
833 } | |
834 | |
835 | |
836 Result VirtualFrame::CallStoreIC() { | |
837 // Name, value, and receiver are on top of the frame. The IC | |
838 // expects name in ecx, value in eax, and receiver on the stack. It | |
839 // does not drop the receiver. | |
840 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
841 Result name = Pop(); | |
842 Result value = Pop(); | |
843 PrepareForCall(1, 0); // One stack arg, not callee-dropped. | |
844 | |
845 if (value.is_register() && value.reg().is(ecx)) { | |
846 if (name.is_register() && name.reg().is(eax)) { | |
847 // Wrong registers. | |
848 __ xchg(eax, ecx); | |
849 } else { | |
850 // Register eax is free for value, which frees ecx for name. | |
851 value.ToRegister(eax); | |
852 name.ToRegister(ecx); | |
853 } | |
854 } else { | |
855 // Register ecx is free for name, which guarantees eax is free for | |
856 // value. | |
857 name.ToRegister(ecx); | |
858 value.ToRegister(eax); | |
859 } | |
860 | |
861 name.Unuse(); | |
862 value.Unuse(); | |
863 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); | |
864 } | |
865 | |
866 | |
867 Result VirtualFrame::CallKeyedStoreIC() { | |
868 // Value, key, and receiver are on the top of the frame. The IC | |
869 // expects value in eax and key and receiver on the stack. It does | |
870 // not drop the key and receiver. | |
871 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
872 // TODO(1222589): Make the IC grab the values from the stack. | |
873 Result value = Pop(); | |
874 PrepareForCall(2, 0); // Two stack args, neither callee-dropped. | |
875 value.ToRegister(eax); | |
876 value.Unuse(); | |
877 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); | |
878 } | |
879 | |
880 | |
881 Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, | |
882 int arg_count, | |
883 int loop_nesting) { | |
884 // Arguments, receiver, and function name are on top of the frame. | |
885 // The IC expects them on the stack. It does not drop the function | |
886 // name slot (but it does drop the rest). | |
887 Handle<Code> ic = (loop_nesting > 0) | |
888 ? cgen_->ComputeCallInitializeInLoop(arg_count) | |
889 : cgen_->ComputeCallInitialize(arg_count); | |
890 // Spill args, receiver, and function. The call will drop args and | |
891 // receiver. | |
892 PrepareForCall(arg_count + 2, arg_count + 1); | |
893 return RawCallCodeObject(ic, mode); | |
894 } | |
895 | |
896 | |
897 Result VirtualFrame::CallConstructor(int arg_count) { | |
898 // Arguments, receiver, and function are on top of the frame. The | |
899 // IC expects arg count in eax, function in edi, and the arguments | |
900 // and receiver on the stack. | |
901 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall)); | |
902 // Duplicate the function before preparing the frame. | |
903 PushElementAt(arg_count + 1); | |
904 Result function = Pop(); | |
905 PrepareForCall(arg_count + 1, arg_count + 1); // Spill args and receiver. | |
906 function.ToRegister(edi); | |
907 | |
908 // Constructors are called with the number of arguments in register | |
909 // eax for now. Another option would be to have separate construct | |
910 // call trampolines per different arguments counts encountered. | |
911 Result num_args = cgen_->allocator()->Allocate(eax); | |
912 ASSERT(num_args.is_valid()); | |
913 __ Set(num_args.reg(), Immediate(arg_count)); | |
914 | |
915 function.Unuse(); | |
916 num_args.Unuse(); | |
917 return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL); | |
918 } | |
919 | |
920 | |
921 void VirtualFrame::Drop(int count) { | |
922 ASSERT(height() >= count); | |
923 int num_virtual_elements = (elements_.length() - 1) - stack_pointer_; | |
924 | |
925 // Emit code to lower the stack pointer if necessary. | |
926 if (num_virtual_elements < count) { | |
927 int num_dropped = count - num_virtual_elements; | |
928 stack_pointer_ -= num_dropped; | |
929 __ add(Operand(esp), Immediate(num_dropped * kPointerSize)); | |
930 } | |
931 | |
932 // Discard elements from the virtual frame and free any registers. | |
933 for (int i = 0; i < count; i++) { | |
934 FrameElement dropped = elements_.RemoveLast(); | |
935 if (dropped.is_register()) { | |
936 Unuse(dropped.reg()); | |
937 } | |
938 } | |
939 } | |
940 | |
941 | |
942 Result VirtualFrame::Pop() { | |
943 FrameElement element = elements_.RemoveLast(); | |
944 int index = elements_.length(); | |
945 ASSERT(element.is_valid()); | |
946 | |
947 bool pop_needed = (stack_pointer_ == index); | |
948 if (pop_needed) { | |
949 stack_pointer_--; | |
950 if (element.is_memory()) { | |
951 Result temp = cgen_->allocator()->Allocate(); | |
952 ASSERT(temp.is_valid()); | |
953 temp.set_static_type(element.static_type()); | |
954 __ pop(temp.reg()); | |
955 return temp; | |
956 } | |
957 | |
958 __ add(Operand(esp), Immediate(kPointerSize)); | |
959 } | |
960 ASSERT(!element.is_memory()); | |
961 | |
962 // The top element is a register, constant, or a copy. Unuse | |
963 // registers and follow copies to their backing store. | |
964 if (element.is_register()) { | |
965 Unuse(element.reg()); | |
966 } else if (element.is_copy()) { | |
967 ASSERT(element.index() < index); | |
968 index = element.index(); | |
969 element = elements_[index]; | |
970 } | |
971 ASSERT(!element.is_copy()); | |
972 | |
973 // The element is memory, a register, or a constant. | |
974 if (element.is_memory()) { | |
975 // Memory elements could only be the backing store of a copy. | |
976 // Allocate the original to a register. | |
977 ASSERT(index <= stack_pointer_); | |
978 Result temp = cgen_->allocator()->Allocate(); | |
979 ASSERT(temp.is_valid()); | |
980 Use(temp.reg(), index); | |
981 FrameElement new_element = | |
982 FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED); | |
983 // Preserve the copy flag on the element. | |
984 if (element.is_copied()) new_element.set_copied(); | |
985 new_element.set_static_type(element.static_type()); | |
986 elements_[index] = new_element; | |
987 __ mov(temp.reg(), Operand(ebp, fp_relative(index))); | |
988 return Result(temp.reg(), cgen_, element.static_type()); | |
989 } else if (element.is_register()) { | |
990 return Result(element.reg(), cgen_, element.static_type()); | |
991 } else { | |
992 ASSERT(element.is_constant()); | |
993 return Result(element.handle(), cgen_); | |
994 } | |
995 } | |
996 | |
997 | |
998 void VirtualFrame::EmitPop(Register reg) { | |
999 ASSERT(stack_pointer_ == elements_.length() - 1); | |
1000 stack_pointer_--; | |
1001 elements_.RemoveLast(); | |
1002 __ pop(reg); | |
1003 } | |
1004 | |
1005 | |
1006 void VirtualFrame::EmitPop(Operand operand) { | |
1007 ASSERT(stack_pointer_ == elements_.length() - 1); | |
1008 stack_pointer_--; | |
1009 elements_.RemoveLast(); | |
1010 __ pop(operand); | |
1011 } | |
1012 | |
1013 | |
1014 void VirtualFrame::EmitPush(Register reg) { | |
1015 ASSERT(stack_pointer_ == elements_.length() - 1); | |
1016 elements_.Add(FrameElement::MemoryElement()); | |
1017 stack_pointer_++; | |
1018 __ push(reg); | |
1019 } | |
1020 | |
1021 | |
1022 void VirtualFrame::EmitPush(Operand operand) { | |
1023 ASSERT(stack_pointer_ == elements_.length() - 1); | |
1024 elements_.Add(FrameElement::MemoryElement()); | |
1025 stack_pointer_++; | |
1026 __ push(operand); | |
1027 } | |
1028 | |
1029 | |
1030 void VirtualFrame::EmitPush(Immediate immediate) { | |
1031 ASSERT(stack_pointer_ == elements_.length() - 1); | |
1032 elements_.Add(FrameElement::MemoryElement()); | |
1033 stack_pointer_++; | |
1034 __ push(immediate); | |
1035 } | |
1036 | |
1037 | |
1038 #undef __ | |
1039 | |
1040 } } // namespace v8::internal | |
OLD | NEW |