blob: a646052eee05faeab51837acce6dc72ec21f96a0 [file] [log] [blame]
Ben Murdoch086aeea2011-05-13 15:57:08 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Ben Murdochb0fe1622011-05-05 13:52:32 +01002// 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
Ben Murdochb8e0da22011-05-16 14:20:40 +010030#if defined(V8_TARGET_ARCH_IA32)
31
Ben Murdochb0fe1622011-05-05 13:52:32 +010032#include "codegen.h"
33#include "deoptimizer.h"
34#include "full-codegen.h"
35#include "safepoint-table.h"
36
37namespace v8 {
38namespace internal {
39
Ben Murdochb0fe1622011-05-05 13:52:32 +010040int Deoptimizer::table_entry_size_ = 10;
41
Steve Block1e0659c2011-05-24 12:43:12 +010042
43int Deoptimizer::patch_size() {
44 return Assembler::kCallInstructionLength;
45}
46
47
48static void ZapCodeRange(Address start, Address end) {
49#ifdef DEBUG
50 ASSERT(start <= end);
51 int size = end - start;
52 CodePatcher destroyer(start, size);
53 while (size-- > 0) destroyer.masm()->int3();
54#endif
55}
56
57
Ben Murdochb0fe1622011-05-05 13:52:32 +010058void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
59 AssertNoAllocation no_allocation;
60
61 if (!function->IsOptimized()) return;
62
63 // Get the optimized code.
64 Code* code = function->code();
Steve Block1e0659c2011-05-24 12:43:12 +010065 Address code_start_address = code->instruction_start();
Ben Murdochb0fe1622011-05-05 13:52:32 +010066
Steve Block1e0659c2011-05-24 12:43:12 +010067 // We will overwrite the code's relocation info in-place. Relocation info
68 // is written backward. The relocation info is the payload of a byte
69 // array. Later on we will slide this to the start of the byte array and
70 // create a filler object in the remaining space.
71 ByteArray* reloc_info = code->relocation_info();
72 Address reloc_end_address = reloc_info->address() + reloc_info->Size();
73 RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
Ben Murdochb0fe1622011-05-05 13:52:32 +010074
Steve Block1e0659c2011-05-24 12:43:12 +010075 // For each return after a safepoint insert a call to the corresponding
76 // deoptimization entry. Since the call is a relative encoding, write new
77 // reloc info. We do not need any of the existing reloc info because the
78 // existing code will not be used again (we zap it in debug builds).
79 SafepointTable table(code);
80 Address prev_address = code_start_address;
81 for (unsigned i = 0; i < table.length(); ++i) {
82 Address curr_address = code_start_address + table.GetPcOffset(i);
83 ZapCodeRange(prev_address, curr_address);
84
Ben Murdochb8e0da22011-05-16 14:20:40 +010085 SafepointEntry safepoint_entry = table.GetEntry(i);
86 int deoptimization_index = safepoint_entry.deoptimization_index();
Ben Murdochb0fe1622011-05-05 13:52:32 +010087 if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) {
Steve Block1e0659c2011-05-24 12:43:12 +010088 // The gap code is needed to get to the state expected at the bailout.
89 curr_address += safepoint_entry.gap_code_size();
90
91 CodePatcher patcher(curr_address, patch_size());
92 Address deopt_entry = GetDeoptimizationEntry(deoptimization_index, LAZY);
93 patcher.masm()->call(deopt_entry, RelocInfo::NONE);
94
95 // We use RUNTIME_ENTRY for deoptimization bailouts.
96 RelocInfo rinfo(curr_address + 1, // 1 after the call opcode.
97 RelocInfo::RUNTIME_ENTRY,
98 reinterpret_cast<intptr_t>(deopt_entry));
99 reloc_info_writer.Write(&rinfo);
100
101 curr_address += patch_size();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100102 }
Steve Block1e0659c2011-05-24 12:43:12 +0100103 prev_address = curr_address;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100104 }
Steve Block1e0659c2011-05-24 12:43:12 +0100105 ZapCodeRange(prev_address,
106 code_start_address + code->safepoint_table_offset());
107
108 // Move the relocation info to the beginning of the byte array.
109 int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
110 memmove(code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
111
112 // The relocation info is in place, update the size.
113 reloc_info->set_length(new_reloc_size);
114
115 // Handle the junk part after the new relocation info. We will create
116 // a non-live object in the extra space at the end of the former reloc info.
117 Address junk_address = reloc_info->address() + reloc_info->Size();
118 ASSERT(junk_address <= reloc_end_address);
119 Heap::CreateFillerObjectAt(junk_address, reloc_end_address - junk_address);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100120
121 // Add the deoptimizing code to the list.
122 DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code);
123 node->set_next(deoptimizing_code_list_);
124 deoptimizing_code_list_ = node;
125
126 // Set the code for the function to non-optimized version.
127 function->ReplaceCode(function->shared()->code());
128
129 if (FLAG_trace_deopt) {
130 PrintF("[forced deoptimization: ");
131 function->PrintName();
132 PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
133 }
134}
135
136
Steve Block1e0659c2011-05-24 12:43:12 +0100137void Deoptimizer::PatchStackCheckCodeAt(Address pc_after,
138 Code* check_code,
139 Code* replacement_code) {
140 Address call_target_address = pc_after - kPointerSize;
141 ASSERT(check_code->entry() ==
142 Assembler::target_address_at(call_target_address));
143 // The stack check code matches the pattern:
144 //
145 // cmp esp, <limit>
146 // jae ok
147 // call <stack guard>
148 // test eax, <loop nesting depth>
149 // ok: ...
150 //
151 // We will patch away the branch so the code is:
152 //
153 // cmp esp, <limit> ;; Not changed
154 // nop
155 // nop
156 // call <on-stack replacment>
157 // test eax, <loop nesting depth>
158 // ok:
159 ASSERT(*(call_target_address - 3) == 0x73 && // jae
160 *(call_target_address - 2) == 0x07 && // offset
161 *(call_target_address - 1) == 0xe8); // call
162 *(call_target_address - 3) = 0x90; // nop
163 *(call_target_address - 2) = 0x90; // nop
164 Assembler::set_target_address_at(call_target_address,
165 replacement_code->entry());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100166}
167
168
Steve Block1e0659c2011-05-24 12:43:12 +0100169void Deoptimizer::RevertStackCheckCodeAt(Address pc_after,
170 Code* check_code,
171 Code* replacement_code) {
172 Address call_target_address = pc_after - kPointerSize;
173 ASSERT(replacement_code->entry() ==
174 Assembler::target_address_at(call_target_address));
Ben Murdoch086aeea2011-05-13 15:57:08 +0100175 // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
176 // restore the conditional branch.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100177 ASSERT(*(call_target_address - 3) == 0x90 && // nop
178 *(call_target_address - 2) == 0x90 && // nop
179 *(call_target_address - 1) == 0xe8); // call
180 *(call_target_address - 3) = 0x73; // jae
Ben Murdoch086aeea2011-05-13 15:57:08 +0100181 *(call_target_address - 2) = 0x07; // offset
Steve Block1e0659c2011-05-24 12:43:12 +0100182 Assembler::set_target_address_at(call_target_address,
183 check_code->entry());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100184}
185
186
187static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) {
188 ByteArray* translations = data->TranslationByteArray();
189 int length = data->DeoptCount();
190 for (int i = 0; i < length; i++) {
191 if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) {
192 TranslationIterator it(translations, data->TranslationIndex(i)->value());
193 int value = it.Next();
194 ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
195 // Read the number of frames.
196 value = it.Next();
197 if (value == 1) return i;
198 }
199 }
200 UNREACHABLE();
201 return -1;
202}
203
204
205void Deoptimizer::DoComputeOsrOutputFrame() {
206 DeoptimizationInputData* data = DeoptimizationInputData::cast(
207 optimized_code_->deoptimization_data());
208 unsigned ast_id = data->OsrAstId()->value();
209 // TODO(kasperl): This should not be the bailout_id_. It should be
210 // the ast id. Confusing.
211 ASSERT(bailout_id_ == ast_id);
212
213 int bailout_id = LookupBailoutId(data, ast_id);
214 unsigned translation_index = data->TranslationIndex(bailout_id)->value();
215 ByteArray* translations = data->TranslationByteArray();
216
217 TranslationIterator iterator(translations, translation_index);
218 Translation::Opcode opcode =
219 static_cast<Translation::Opcode>(iterator.Next());
220 ASSERT(Translation::BEGIN == opcode);
221 USE(opcode);
222 int count = iterator.Next();
223 ASSERT(count == 1);
224 USE(count);
225
226 opcode = static_cast<Translation::Opcode>(iterator.Next());
227 USE(opcode);
228 ASSERT(Translation::FRAME == opcode);
229 unsigned node_id = iterator.Next();
230 USE(node_id);
231 ASSERT(node_id == ast_id);
232 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator.Next()));
233 USE(function);
234 ASSERT(function == function_);
235 unsigned height = iterator.Next();
236 unsigned height_in_bytes = height * kPointerSize;
237 USE(height_in_bytes);
238
239 unsigned fixed_size = ComputeFixedSize(function_);
240 unsigned input_frame_size = input_->GetFrameSize();
241 ASSERT(fixed_size + height_in_bytes == input_frame_size);
242
243 unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize;
244 unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
245 unsigned outgoing_size = outgoing_height * kPointerSize;
246 unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
247 ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call.
248
249 if (FLAG_trace_osr) {
250 PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
251 reinterpret_cast<intptr_t>(function_));
252 function_->PrintName();
253 PrintF(" => node=%u, frame=%d->%d]\n",
254 ast_id,
255 input_frame_size,
256 output_frame_size);
257 }
258
259 // There's only one output frame in the OSR case.
260 output_count_ = 1;
261 output_ = new FrameDescription*[1];
262 output_[0] = new(output_frame_size) FrameDescription(
263 output_frame_size, function_);
264
265 // Clear the incoming parameters in the optimized frame to avoid
266 // confusing the garbage collector.
267 unsigned output_offset = output_frame_size - kPointerSize;
268 int parameter_count = function_->shared()->formal_parameter_count() + 1;
269 for (int i = 0; i < parameter_count; ++i) {
270 output_[0]->SetFrameSlot(output_offset, 0);
271 output_offset -= kPointerSize;
272 }
273
274 // Translate the incoming parameters. This may overwrite some of the
275 // incoming argument slots we've just cleared.
276 int input_offset = input_frame_size - kPointerSize;
277 bool ok = true;
278 int limit = input_offset - (parameter_count * kPointerSize);
279 while (ok && input_offset > limit) {
280 ok = DoOsrTranslateCommand(&iterator, &input_offset);
281 }
282
283 // There are no translation commands for the caller's pc and fp, the
284 // context, and the function. Set them up explicitly.
285 for (int i = 0; ok && i < 4; i++) {
286 uint32_t input_value = input_->GetFrameSlot(input_offset);
287 if (FLAG_trace_osr) {
288 PrintF(" [esp + %d] <- 0x%08x ; [esp + %d] (fixed part)\n",
289 output_offset,
290 input_value,
291 input_offset);
292 }
293 output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
294 input_offset -= kPointerSize;
295 output_offset -= kPointerSize;
296 }
297
298 // Translate the rest of the frame.
299 while (ok && input_offset >= 0) {
300 ok = DoOsrTranslateCommand(&iterator, &input_offset);
301 }
302
303 // If translation of any command failed, continue using the input frame.
304 if (!ok) {
305 delete output_[0];
306 output_[0] = input_;
307 output_[0]->SetPc(reinterpret_cast<uint32_t>(from_));
308 } else {
309 // Setup the frame pointer and the context pointer.
310 output_[0]->SetRegister(ebp.code(), input_->GetRegister(ebp.code()));
311 output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code()));
312
313 unsigned pc_offset = data->OsrPcOffset()->value();
314 uint32_t pc = reinterpret_cast<uint32_t>(
315 optimized_code_->entry() + pc_offset);
316 output_[0]->SetPc(pc);
317 }
318 Code* continuation = Builtins::builtin(Builtins::NotifyOSR);
319 output_[0]->SetContinuation(
320 reinterpret_cast<uint32_t>(continuation->entry()));
321
322 if (FLAG_trace_osr) {
323 PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
324 ok ? "finished" : "aborted",
325 reinterpret_cast<intptr_t>(function));
326 function->PrintName();
327 PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
328 }
329}
330
331
332void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
333 int frame_index) {
334 // Read the ast node id, function, and frame height for this output frame.
335 Translation::Opcode opcode =
336 static_cast<Translation::Opcode>(iterator->Next());
337 USE(opcode);
338 ASSERT(Translation::FRAME == opcode);
339 int node_id = iterator->Next();
340 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
341 unsigned height = iterator->Next();
342 unsigned height_in_bytes = height * kPointerSize;
343 if (FLAG_trace_deopt) {
344 PrintF(" translating ");
345 function->PrintName();
346 PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes);
347 }
348
349 // The 'fixed' part of the frame consists of the incoming parameters and
350 // the part described by JavaScriptFrameConstants.
351 unsigned fixed_frame_size = ComputeFixedSize(function);
352 unsigned input_frame_size = input_->GetFrameSize();
353 unsigned output_frame_size = height_in_bytes + fixed_frame_size;
354
355 // Allocate and store the output frame description.
356 FrameDescription* output_frame =
357 new(output_frame_size) FrameDescription(output_frame_size, function);
358
359 bool is_bottommost = (0 == frame_index);
360 bool is_topmost = (output_count_ - 1 == frame_index);
361 ASSERT(frame_index >= 0 && frame_index < output_count_);
362 ASSERT(output_[frame_index] == NULL);
363 output_[frame_index] = output_frame;
364
365 // The top address for the bottommost output frame can be computed from
366 // the input frame pointer and the output frame's height. For all
367 // subsequent output frames, it can be computed from the previous one's
368 // top address and the current frame's size.
369 uint32_t top_address;
370 if (is_bottommost) {
371 // 2 = context and function in the frame.
372 top_address =
373 input_->GetRegister(ebp.code()) - (2 * kPointerSize) - height_in_bytes;
374 } else {
375 top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
376 }
377 output_frame->SetTop(top_address);
378
379 // Compute the incoming parameter translation.
380 int parameter_count = function->shared()->formal_parameter_count() + 1;
381 unsigned output_offset = output_frame_size;
382 unsigned input_offset = input_frame_size;
383 for (int i = 0; i < parameter_count; ++i) {
384 output_offset -= kPointerSize;
385 DoTranslateCommand(iterator, frame_index, output_offset);
386 }
387 input_offset -= (parameter_count * kPointerSize);
388
389 // There are no translation commands for the caller's pc and fp, the
390 // context, and the function. Synthesize their values and set them up
391 // explicitly.
392 //
393 // The caller's pc for the bottommost output frame is the same as in the
394 // input frame. For all subsequent output frames, it can be read from the
395 // previous one. This frame's pc can be computed from the non-optimized
396 // function code and AST id of the bailout.
397 output_offset -= kPointerSize;
398 input_offset -= kPointerSize;
399 intptr_t value;
400 if (is_bottommost) {
401 value = input_->GetFrameSlot(input_offset);
402 } else {
403 value = output_[frame_index - 1]->GetPc();
404 }
405 output_frame->SetFrameSlot(output_offset, value);
406 if (FLAG_trace_deopt) {
407 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
408 top_address + output_offset, output_offset, value);
409 }
410
411 // The caller's frame pointer for the bottommost output frame is the same
412 // as in the input frame. For all subsequent output frames, it can be
413 // read from the previous one. Also compute and set this frame's frame
414 // pointer.
415 output_offset -= kPointerSize;
416 input_offset -= kPointerSize;
417 if (is_bottommost) {
418 value = input_->GetFrameSlot(input_offset);
419 } else {
420 value = output_[frame_index - 1]->GetFp();
421 }
422 output_frame->SetFrameSlot(output_offset, value);
423 intptr_t fp_value = top_address + output_offset;
424 ASSERT(!is_bottommost || input_->GetRegister(ebp.code()) == fp_value);
425 output_frame->SetFp(fp_value);
426 if (is_topmost) output_frame->SetRegister(ebp.code(), fp_value);
427 if (FLAG_trace_deopt) {
428 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
429 fp_value, output_offset, value);
430 }
431
432 // The context can be gotten from the function so long as we don't
433 // optimize functions that need local contexts.
434 output_offset -= kPointerSize;
435 input_offset -= kPointerSize;
436 value = reinterpret_cast<uint32_t>(function->context());
437 // The context for the bottommost output frame should also agree with the
438 // input frame.
439 ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
440 output_frame->SetFrameSlot(output_offset, value);
441 if (is_topmost) output_frame->SetRegister(esi.code(), value);
442 if (FLAG_trace_deopt) {
443 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n",
444 top_address + output_offset, output_offset, value);
445 }
446
447 // The function was mentioned explicitly in the BEGIN_FRAME.
448 output_offset -= kPointerSize;
449 input_offset -= kPointerSize;
450 value = reinterpret_cast<uint32_t>(function);
451 // The function for the bottommost output frame should also agree with the
452 // input frame.
453 ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
454 output_frame->SetFrameSlot(output_offset, value);
455 if (FLAG_trace_deopt) {
456 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n",
457 top_address + output_offset, output_offset, value);
458 }
459
460 // Translate the rest of the frame.
461 for (unsigned i = 0; i < height; ++i) {
462 output_offset -= kPointerSize;
463 DoTranslateCommand(iterator, frame_index, output_offset);
464 }
465 ASSERT(0 == output_offset);
466
467 // Compute this frame's PC, state, and continuation.
468 Code* non_optimized_code = function->shared()->code();
469 FixedArray* raw_data = non_optimized_code->deoptimization_data();
470 DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
471 Address start = non_optimized_code->instruction_start();
472 unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
473 unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
474 uint32_t pc_value = reinterpret_cast<uint32_t>(start + pc_offset);
475 output_frame->SetPc(pc_value);
476
477 FullCodeGenerator::State state =
478 FullCodeGenerator::StateField::decode(pc_and_state);
479 output_frame->SetState(Smi::FromInt(state));
480
481 // Set the continuation for the topmost frame.
482 if (is_topmost) {
483 Code* continuation = (bailout_type_ == EAGER)
484 ? Builtins::builtin(Builtins::NotifyDeoptimized)
485 : Builtins::builtin(Builtins::NotifyLazyDeoptimized);
486 output_frame->SetContinuation(
487 reinterpret_cast<uint32_t>(continuation->entry()));
488 }
489
490 if (output_count_ - 1 == frame_index) iterator->Done();
491}
492
493
494#define __ masm()->
495
496void Deoptimizer::EntryGenerator::Generate() {
497 GeneratePrologue();
498 CpuFeatures::Scope scope(SSE2);
499
500 // Save all general purpose registers before messing with them.
501 const int kNumberOfRegisters = Register::kNumRegisters;
502
503 const int kDoubleRegsSize = kDoubleSize *
504 XMMRegister::kNumAllocatableRegisters;
505 __ sub(Operand(esp), Immediate(kDoubleRegsSize));
506 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
507 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
508 int offset = i * kDoubleSize;
509 __ movdbl(Operand(esp, offset), xmm_reg);
510 }
511
512 __ pushad();
513
514 const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
515 kDoubleRegsSize;
516
517 // Get the bailout id from the stack.
518 __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
519
520 // Get the address of the location in the code object if possible
521 // and compute the fp-to-sp delta in register edx.
522 if (type() == EAGER) {
523 __ Set(ecx, Immediate(0));
524 __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
525 } else {
526 __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
527 __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
528 }
529 __ sub(edx, Operand(ebp));
530 __ neg(edx);
531
532 // Allocate a new deoptimizer object.
533 __ PrepareCallCFunction(5, eax);
534 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
535 __ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
536 __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
537 __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
538 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
539 __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
540 __ CallCFunction(ExternalReference::new_deoptimizer_function(), 5);
541
542 // Preserve deoptimizer object in register eax and get the input
543 // frame descriptor pointer.
544 __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
545
546 // Fill in the input registers.
Steve Block1e0659c2011-05-24 12:43:12 +0100547 for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
548 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
549 __ pop(Operand(ebx, offset));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100550 }
551
552 // Fill in the double input registers.
553 int double_regs_offset = FrameDescription::double_registers_offset();
554 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
555 int dst_offset = i * kDoubleSize + double_regs_offset;
Steve Block1e0659c2011-05-24 12:43:12 +0100556 int src_offset = i * kDoubleSize;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100557 __ movdbl(xmm0, Operand(esp, src_offset));
558 __ movdbl(Operand(ebx, dst_offset), xmm0);
559 }
560
Steve Block1e0659c2011-05-24 12:43:12 +0100561 // Remove the bailout id and the double registers from the stack.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100562 if (type() == EAGER) {
Steve Block1e0659c2011-05-24 12:43:12 +0100563 __ add(Operand(esp), Immediate(kDoubleRegsSize + kPointerSize));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100564 } else {
Steve Block1e0659c2011-05-24 12:43:12 +0100565 __ add(Operand(esp), Immediate(kDoubleRegsSize + 2 * kPointerSize));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100566 }
567
568 // Compute a pointer to the unwinding limit in register ecx; that is
569 // the first stack slot not part of the input frame.
570 __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
571 __ add(ecx, Operand(esp));
572
573 // Unwind the stack down to - but not including - the unwinding
574 // limit and copy the contents of the activation frame to the input
575 // frame description.
576 __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset()));
577 Label pop_loop;
578 __ bind(&pop_loop);
579 __ pop(Operand(edx, 0));
580 __ add(Operand(edx), Immediate(sizeof(uint32_t)));
581 __ cmp(ecx, Operand(esp));
582 __ j(not_equal, &pop_loop);
583
584 // Compute the output frame in the deoptimizer.
585 __ push(eax);
586 __ PrepareCallCFunction(1, ebx);
587 __ mov(Operand(esp, 0 * kPointerSize), eax);
588 __ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
589 __ pop(eax);
590
591 // Replace the current frame with the output frames.
592 Label outer_push_loop, inner_push_loop;
593 // Outer loop state: eax = current FrameDescription**, edx = one past the
594 // last FrameDescription**.
595 __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
596 __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
597 __ lea(edx, Operand(eax, edx, times_4, 0));
598 __ bind(&outer_push_loop);
599 // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
600 __ mov(ebx, Operand(eax, 0));
601 __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
602 __ bind(&inner_push_loop);
603 __ sub(Operand(ecx), Immediate(sizeof(uint32_t)));
604 __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset()));
605 __ test(ecx, Operand(ecx));
606 __ j(not_zero, &inner_push_loop);
607 __ add(Operand(eax), Immediate(kPointerSize));
608 __ cmp(eax, Operand(edx));
609 __ j(below, &outer_push_loop);
610
611 // In case of OSR, we have to restore the XMM registers.
612 if (type() == OSR) {
613 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
614 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
615 int src_offset = i * kDoubleSize + double_regs_offset;
616 __ movdbl(xmm_reg, Operand(ebx, src_offset));
617 }
618 }
619
620 // Push state, pc, and continuation from the last output frame.
621 if (type() != OSR) {
622 __ push(Operand(ebx, FrameDescription::state_offset()));
623 }
624 __ push(Operand(ebx, FrameDescription::pc_offset()));
625 __ push(Operand(ebx, FrameDescription::continuation_offset()));
626
627
628 // Push the registers from the last output frame.
629 for (int i = 0; i < kNumberOfRegisters; i++) {
Steve Block1e0659c2011-05-24 12:43:12 +0100630 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100631 __ push(Operand(ebx, offset));
632 }
633
634 // Restore the registers from the stack.
635 __ popad();
636
637 // Return to the continuation point.
638 __ ret(0);
639}
640
641
642void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
643 // Create a sequence of deoptimization entries.
644 Label done;
645 for (int i = 0; i < count(); i++) {
646 int start = masm()->pc_offset();
647 USE(start);
648 __ push_imm32(i);
649 __ jmp(&done);
650 ASSERT(masm()->pc_offset() - start == table_entry_size_);
651 }
652 __ bind(&done);
653}
654
655#undef __
656
657
658} } // namespace v8::internal
Ben Murdochb8e0da22011-05-16 14:20:40 +0100659
660#endif // V8_TARGET_ARCH_IA32