1// Copyright 2012 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 V8_TARGET_ARCH_IA32
31
32#include "codegen.h"
33#include "deoptimizer.h"
34#include "full-codegen.h"
35#include "safepoint-table.h"
36
37namespace v8 {
38namespace internal {
39
40const int Deoptimizer::table_entry_size_ = 10;
41
42
43int Deoptimizer::patch_size() {
44  return Assembler::kCallInstructionLength;
45}
46
47
48void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
49  Isolate* isolate = code->GetIsolate();
50  HandleScope scope(isolate);
51
52  // Compute the size of relocation information needed for the code
53  // patching in Deoptimizer::DeoptimizeFunction.
54  int min_reloc_size = 0;
55  int prev_pc_offset = 0;
56  DeoptimizationInputData* deopt_data =
57      DeoptimizationInputData::cast(code->deoptimization_data());
58  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
59    int pc_offset = deopt_data->Pc(i)->value();
60    if (pc_offset == -1) continue;
61    ASSERT_GE(pc_offset, prev_pc_offset);
62    int pc_delta = pc_offset - prev_pc_offset;
63    // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
64    // if encodable with small pc delta encoding and up to 6 bytes
65    // otherwise.
66    if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
67      min_reloc_size += 2;
68    } else {
69      min_reloc_size += 6;
70    }
71    prev_pc_offset = pc_offset;
72  }
73
74  // If the relocation information is not big enough we create a new
75  // relocation info object that is padded with comments to make it
76  // big enough for lazy doptimization.
77  int reloc_length = code->relocation_info()->length();
78  if (min_reloc_size > reloc_length) {
79    int comment_reloc_size = RelocInfo::kMinRelocCommentSize;
80    // Padding needed.
81    int min_padding = min_reloc_size - reloc_length;
82    // Number of comments needed to take up at least that much space.
83    int additional_comments =
84        (min_padding + comment_reloc_size - 1) / comment_reloc_size;
85    // Actual padding size.
86    int padding = additional_comments * comment_reloc_size;
87    // Allocate new relocation info and copy old relocation to the end
88    // of the new relocation info array because relocation info is
89    // written and read backwards.
90    Factory* factory = isolate->factory();
91    Handle<ByteArray> new_reloc =
92        factory->NewByteArray(reloc_length + padding, TENURED);
93    OS::MemCopy(new_reloc->GetDataStartAddress() + padding,
94                code->relocation_info()->GetDataStartAddress(),
95                reloc_length);
96    // Create a relocation writer to write the comments in the padding
97    // space. Use position 0 for everything to ensure short encoding.
98    RelocInfoWriter reloc_info_writer(
99        new_reloc->GetDataStartAddress() + padding, 0);
100    intptr_t comment_string
101        = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString);
102    RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL);
103    for (int i = 0; i < additional_comments; ++i) {
104#ifdef DEBUG
105      byte* pos_before = reloc_info_writer.pos();
106#endif
107      reloc_info_writer.Write(&rinfo);
108      ASSERT(RelocInfo::kMinRelocCommentSize ==
109             pos_before - reloc_info_writer.pos());
110    }
111    // Replace relocation information on the code object.
112    code->set_relocation_info(*new_reloc);
113  }
114}
115
116
117void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
118  Address code_start_address = code->instruction_start();
119  // We will overwrite the code's relocation info in-place. Relocation info
120  // is written backward. The relocation info is the payload of a byte
121  // array.  Later on we will slide this to the start of the byte array and
122  // create a filler object in the remaining space.
123  ByteArray* reloc_info = code->relocation_info();
124  Address reloc_end_address = reloc_info->address() + reloc_info->Size();
125  RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
126
127  // For each LLazyBailout instruction insert a call to the corresponding
128  // deoptimization entry.
129
130  // Since the call is a relative encoding, write new
131  // reloc info.  We do not need any of the existing reloc info because the
132  // existing code will not be used again (we zap it in debug builds).
133  //
134  // Emit call to lazy deoptimization at all lazy deopt points.
135  DeoptimizationInputData* deopt_data =
136      DeoptimizationInputData::cast(code->deoptimization_data());
137#ifdef DEBUG
138  Address prev_call_address = NULL;
139#endif
140  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
141    if (deopt_data->Pc(i)->value() == -1) continue;
142    // Patch lazy deoptimization entry.
143    Address call_address = code_start_address + deopt_data->Pc(i)->value();
144    CodePatcher patcher(call_address, patch_size());
145    Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
146    patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
147    // We use RUNTIME_ENTRY for deoptimization bailouts.
148    RelocInfo rinfo(call_address + 1,  // 1 after the call opcode.
149                    RelocInfo::RUNTIME_ENTRY,
150                    reinterpret_cast<intptr_t>(deopt_entry),
151                    NULL);
152    reloc_info_writer.Write(&rinfo);
153    ASSERT_GE(reloc_info_writer.pos(),
154              reloc_info->address() + ByteArray::kHeaderSize);
155    ASSERT(prev_call_address == NULL ||
156           call_address >= prev_call_address + patch_size());
157    ASSERT(call_address + patch_size() <= code->instruction_end());
158#ifdef DEBUG
159    prev_call_address = call_address;
160#endif
161  }
162
163  // Move the relocation info to the beginning of the byte array.
164  int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
165  OS::MemMove(
166      code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
167
168  // The relocation info is in place, update the size.
169  reloc_info->set_length(new_reloc_size);
170
171  // Handle the junk part after the new relocation info. We will create
172  // a non-live object in the extra space at the end of the former reloc info.
173  Address junk_address = reloc_info->address() + reloc_info->Size();
174  ASSERT(junk_address <= reloc_end_address);
175  isolate->heap()->CreateFillerObjectAt(junk_address,
176                                        reloc_end_address - junk_address);
177}
178
179
180static const byte kJnsInstruction = 0x79;
181static const byte kJnsOffset = 0x11;
182static const byte kCallInstruction = 0xe8;
183static const byte kNopByteOne = 0x66;
184static const byte kNopByteTwo = 0x90;
185
186// The back edge bookkeeping code matches the pattern:
187//
188//     sub <profiling_counter>, <delta>
189//     jns ok
190//     call <interrupt stub>
191//   ok:
192//
193// The patched back edge looks like this:
194//
195//     sub <profiling_counter>, <delta>  ;; Not changed
196//     nop
197//     nop
198//     call <on-stack replacment>
199//   ok:
200
201void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
202                                       Address pc_after,
203                                       Code* interrupt_code,
204                                       Code* replacement_code) {
205  ASSERT(!InterruptCodeIsPatched(unoptimized_code,
206                                 pc_after,
207                                 interrupt_code,
208                                 replacement_code));
209  // Turn the jump into nops.
210  Address call_target_address = pc_after - kIntSize;
211  *(call_target_address - 3) = kNopByteOne;
212  *(call_target_address - 2) = kNopByteTwo;
213  // Replace the call address.
214  Assembler::set_target_address_at(call_target_address,
215                                   replacement_code->entry());
216
217  unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
218      unoptimized_code, call_target_address, replacement_code);
219}
220
221
222void Deoptimizer::RevertInterruptCodeAt(Code* unoptimized_code,
223                                        Address pc_after,
224                                        Code* interrupt_code,
225                                        Code* replacement_code) {
226  ASSERT(InterruptCodeIsPatched(unoptimized_code,
227                                pc_after,
228                                interrupt_code,
229                                replacement_code));
230  // Restore the original jump.
231  Address call_target_address = pc_after - kIntSize;
232  *(call_target_address - 3) = kJnsInstruction;
233  *(call_target_address - 2) = kJnsOffset;
234  // Restore the original call address.
235  Assembler::set_target_address_at(call_target_address,
236                                   interrupt_code->entry());
237
238  interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
239      unoptimized_code, call_target_address, interrupt_code);
240}
241
242
243#ifdef DEBUG
244bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
245                                         Address pc_after,
246                                         Code* interrupt_code,
247                                         Code* replacement_code) {
248  Address call_target_address = pc_after - kIntSize;
249  ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
250  if (*(call_target_address - 3) == kNopByteOne) {
251    ASSERT_EQ(replacement_code->entry(),
252             Assembler::target_address_at(call_target_address));
253    ASSERT_EQ(kNopByteTwo,      *(call_target_address - 2));
254    return true;
255  } else {
256    ASSERT_EQ(interrupt_code->entry(),
257              Assembler::target_address_at(call_target_address));
258    ASSERT_EQ(kJnsInstruction,  *(call_target_address - 3));
259    ASSERT_EQ(kJnsOffset,       *(call_target_address - 2));
260    return false;
261  }
262}
263#endif  // DEBUG
264
265
266static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
267  ByteArray* translations = data->TranslationByteArray();
268  int length = data->DeoptCount();
269  for (int i = 0; i < length; i++) {
270    if (data->AstId(i) == ast_id) {
271      TranslationIterator it(translations,  data->TranslationIndex(i)->value());
272      int value = it.Next();
273      ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
274      // Read the number of frames.
275      value = it.Next();
276      if (value == 1) return i;
277    }
278  }
279  UNREACHABLE();
280  return -1;
281}
282
283
284void Deoptimizer::DoComputeOsrOutputFrame() {
285  DeoptimizationInputData* data = DeoptimizationInputData::cast(
286      compiled_code_->deoptimization_data());
287  unsigned ast_id = data->OsrAstId()->value();
288  // TODO(kasperl): This should not be the bailout_id_. It should be
289  // the ast id. Confusing.
290  ASSERT(bailout_id_ == ast_id);
291
292  int bailout_id = LookupBailoutId(data, BailoutId(ast_id));
293  unsigned translation_index = data->TranslationIndex(bailout_id)->value();
294  ByteArray* translations = data->TranslationByteArray();
295
296  TranslationIterator iterator(translations, translation_index);
297  Translation::Opcode opcode =
298      static_cast<Translation::Opcode>(iterator.Next());
299  ASSERT(Translation::BEGIN == opcode);
300  USE(opcode);
301  int count = iterator.Next();
302  iterator.Next();  // Drop JS frames count.
303  ASSERT(count == 1);
304  USE(count);
305
306  opcode = static_cast<Translation::Opcode>(iterator.Next());
307  USE(opcode);
308  ASSERT(Translation::JS_FRAME == opcode);
309  unsigned node_id = iterator.Next();
310  USE(node_id);
311  ASSERT(node_id == ast_id);
312  int closure_id = iterator.Next();
313  USE(closure_id);
314  ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
315  unsigned height = iterator.Next();
316  unsigned height_in_bytes = height * kPointerSize;
317  USE(height_in_bytes);
318
319  unsigned fixed_size = ComputeFixedSize(function_);
320  unsigned input_frame_size = input_->GetFrameSize();
321  ASSERT(fixed_size + height_in_bytes == input_frame_size);
322
323  unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize;
324  unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
325  unsigned outgoing_size = outgoing_height * kPointerSize;
326  unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
327  ASSERT(outgoing_size == 0);  // OSR does not happen in the middle of a call.
328
329  if (FLAG_trace_osr) {
330    PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
331           reinterpret_cast<intptr_t>(function_));
332    PrintFunctionName();
333    PrintF(" => node=%u, frame=%d->%d, ebp:esp=0x%08x:0x%08x]\n",
334           ast_id,
335           input_frame_size,
336           output_frame_size,
337           input_->GetRegister(ebp.code()),
338           input_->GetRegister(esp.code()));
339  }
340
341  // There's only one output frame in the OSR case.
342  output_count_ = 1;
343  output_ = new FrameDescription*[1];
344  output_[0] = new(output_frame_size) FrameDescription(
345      output_frame_size, function_);
346  output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
347
348  // Clear the incoming parameters in the optimized frame to avoid
349  // confusing the garbage collector.
350  unsigned output_offset = output_frame_size - kPointerSize;
351  int parameter_count = function_->shared()->formal_parameter_count() + 1;
352  for (int i = 0; i < parameter_count; ++i) {
353    output_[0]->SetFrameSlot(output_offset, 0);
354    output_offset -= kPointerSize;
355  }
356
357  // Translate the incoming parameters. This may overwrite some of the
358  // incoming argument slots we've just cleared.
359  int input_offset = input_frame_size - kPointerSize;
360  bool ok = true;
361  int limit = input_offset - (parameter_count * kPointerSize);
362  while (ok && input_offset > limit) {
363    ok = DoOsrTranslateCommand(&iterator, &input_offset);
364  }
365
366  // There are no translation commands for the caller's pc and fp, the
367  // context, and the function.  Set them up explicitly.
368  for (int i =  StandardFrameConstants::kCallerPCOffset;
369       ok && i >=  StandardFrameConstants::kMarkerOffset;
370       i -= kPointerSize) {
371    uint32_t input_value = input_->GetFrameSlot(input_offset);
372    if (FLAG_trace_osr) {
373      const char* name = "UNKNOWN";
374      switch (i) {
375        case StandardFrameConstants::kCallerPCOffset:
376          name = "caller's pc";
377          break;
378        case StandardFrameConstants::kCallerFPOffset:
379          name = "fp";
380          break;
381        case StandardFrameConstants::kContextOffset:
382          name = "context";
383          break;
384        case StandardFrameConstants::kMarkerOffset:
385          name = "function";
386          break;
387      }
388      PrintF("    [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n",
389             output_offset,
390             input_value,
391             input_offset,
392             name);
393    }
394    output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
395    input_offset -= kPointerSize;
396    output_offset -= kPointerSize;
397  }
398
399  // All OSR stack frames are dynamically aligned to an 8-byte boundary.
400  int frame_pointer = input_->GetRegister(ebp.code());
401  if ((frame_pointer & kPointerSize) != 0) {
402    frame_pointer -= kPointerSize;
403    has_alignment_padding_ = 1;
404  }
405
406  int32_t alignment_state = (has_alignment_padding_ == 1) ?
407    kAlignmentPaddingPushed :
408    kNoAlignmentPadding;
409  if (FLAG_trace_osr) {
410    PrintF("    [sp + %d] <- 0x%08x ; (alignment state)\n",
411           output_offset,
412           alignment_state);
413  }
414  output_[0]->SetFrameSlot(output_offset, alignment_state);
415  output_offset -= kPointerSize;
416
417  // Translate the rest of the frame.
418  while (ok && input_offset >= 0) {
419    ok = DoOsrTranslateCommand(&iterator, &input_offset);
420  }
421
422  // If translation of any command failed, continue using the input frame.
423  if (!ok) {
424    delete output_[0];
425    output_[0] = input_;
426    output_[0]->SetPc(reinterpret_cast<uint32_t>(from_));
427  } else {
428    // Set up the frame pointer and the context pointer.
429    output_[0]->SetRegister(ebp.code(), frame_pointer);
430    output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code()));
431
432    unsigned pc_offset = data->OsrPcOffset()->value();
433    uint32_t pc = reinterpret_cast<uint32_t>(
434        compiled_code_->entry() + pc_offset);
435    output_[0]->SetPc(pc);
436  }
437  Code* continuation =
438      function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
439  output_[0]->SetContinuation(
440      reinterpret_cast<uint32_t>(continuation->entry()));
441
442  if (FLAG_trace_osr) {
443    PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
444           ok ? "finished" : "aborted",
445           reinterpret_cast<intptr_t>(function_));
446    PrintFunctionName();
447    PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
448  }
449}
450
451
452void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
453  // Set the register values. The values are not important as there are no
454  // callee saved registers in JavaScript frames, so all registers are
455  // spilled. Registers ebp and esp are set to the correct values though.
456
457  for (int i = 0; i < Register::kNumRegisters; i++) {
458    input_->SetRegister(i, i * 4);
459  }
460  input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
461  input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
462  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
463    input_->SetDoubleRegister(i, 0.0);
464  }
465
466  // Fill the frame content from the actual data on the frame.
467  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
468    input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
469  }
470}
471
472
473void Deoptimizer::SetPlatformCompiledStubRegisters(
474    FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
475  intptr_t handler =
476      reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
477  int params = descriptor->register_param_count_;
478  if (descriptor->stack_parameter_count_ != NULL) {
479    params++;
480  }
481  output_frame->SetRegister(eax.code(), params);
482  output_frame->SetRegister(ebx.code(), handler);
483}
484
485
486void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
487  for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
488    double double_value = input_->GetDoubleRegister(i);
489    output_frame->SetDoubleRegister(i, double_value);
490  }
491}
492
493
494bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
495  int parameter_count = function->shared()->formal_parameter_count() + 1;
496  unsigned input_frame_size = input_->GetFrameSize();
497  unsigned alignment_state_offset =
498      input_frame_size - parameter_count * kPointerSize -
499      StandardFrameConstants::kFixedFrameSize -
500      kPointerSize;
501  ASSERT(JavaScriptFrameConstants::kDynamicAlignmentStateOffset ==
502      JavaScriptFrameConstants::kLocal0Offset);
503  int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
504  return (alignment_state == kAlignmentPaddingPushed);
505}
506
507
508#define __ masm()->
509
510void Deoptimizer::EntryGenerator::Generate() {
511  GeneratePrologue();
512
513  // Save all general purpose registers before messing with them.
514  const int kNumberOfRegisters = Register::kNumRegisters;
515
516  const int kDoubleRegsSize = kDoubleSize *
517                              XMMRegister::kNumAllocatableRegisters;
518  __ sub(esp, Immediate(kDoubleRegsSize));
519  if (CpuFeatures::IsSupported(SSE2)) {
520    CpuFeatureScope scope(masm(), SSE2);
521    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
522      XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
523      int offset = i * kDoubleSize;
524      __ movdbl(Operand(esp, offset), xmm_reg);
525    }
526  }
527
528  __ pushad();
529
530  const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
531                                      kDoubleRegsSize;
532
533  // Get the bailout id from the stack.
534  __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
535
536  // Get the address of the location in the code object
537  // and compute the fp-to-sp delta in register edx.
538  __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
539  __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
540
541  __ sub(edx, ebp);
542  __ neg(edx);
543
544  // Allocate a new deoptimizer object.
545  __ PrepareCallCFunction(6, eax);
546  __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
547  __ mov(Operand(esp, 0 * kPointerSize), eax);  // Function.
548  __ mov(Operand(esp, 1 * kPointerSize), Immediate(type()));  // Bailout type.
549  __ mov(Operand(esp, 2 * kPointerSize), ebx);  // Bailout id.
550  __ mov(Operand(esp, 3 * kPointerSize), ecx);  // Code address or 0.
551  __ mov(Operand(esp, 4 * kPointerSize), edx);  // Fp-to-sp delta.
552  __ mov(Operand(esp, 5 * kPointerSize),
553         Immediate(ExternalReference::isolate_address(isolate())));
554  {
555    AllowExternalCallThatCantCauseGC scope(masm());
556    __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
557  }
558
559  // Preserve deoptimizer object in register eax and get the input
560  // frame descriptor pointer.
561  __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
562
563  // Fill in the input registers.
564  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
565    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
566    __ pop(Operand(ebx, offset));
567  }
568
569  int double_regs_offset = FrameDescription::double_registers_offset();
570  if (CpuFeatures::IsSupported(SSE2)) {
571    CpuFeatureScope scope(masm(), SSE2);
572    // Fill in the double input registers.
573    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
574      int dst_offset = i * kDoubleSize + double_regs_offset;
575      int src_offset = i * kDoubleSize;
576      __ movdbl(xmm0, Operand(esp, src_offset));
577      __ movdbl(Operand(ebx, dst_offset), xmm0);
578    }
579  }
580
581  // Clear FPU all exceptions.
582  // TODO(ulan): Find out why the TOP register is not zero here in some cases,
583  // and check that the generated code never deoptimizes with unbalanced stack.
584  __ fnclex();
585
586  // Remove the bailout id, return address and the double registers.
587  __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
588
589  // Compute a pointer to the unwinding limit in register ecx; that is
590  // the first stack slot not part of the input frame.
591  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
592  __ add(ecx, esp);
593
594  // Unwind the stack down to - but not including - the unwinding
595  // limit and copy the contents of the activation frame to the input
596  // frame description.
597  __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset()));
598  Label pop_loop_header;
599  __ jmp(&pop_loop_header);
600  Label pop_loop;
601  __ bind(&pop_loop);
602  __ pop(Operand(edx, 0));
603  __ add(edx, Immediate(sizeof(uint32_t)));
604  __ bind(&pop_loop_header);
605  __ cmp(ecx, esp);
606  __ j(not_equal, &pop_loop);
607
608  // Compute the output frame in the deoptimizer.
609  __ push(eax);
610  __ PrepareCallCFunction(1, ebx);
611  __ mov(Operand(esp, 0 * kPointerSize), eax);
612  {
613    AllowExternalCallThatCantCauseGC scope(masm());
614    __ CallCFunction(
615        ExternalReference::compute_output_frames_function(isolate()), 1);
616  }
617  __ pop(eax);
618
619  if (type() != OSR) {
620    // If frame was dynamically aligned, pop padding.
621    Label no_padding;
622    __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
623           Immediate(0));
624    __ j(equal, &no_padding);
625    __ pop(ecx);
626    if (FLAG_debug_code) {
627      __ cmp(ecx, Immediate(kAlignmentZapValue));
628      __ Assert(equal, kAlignmentMarkerExpected);
629    }
630    __ bind(&no_padding);
631  } else {
632    // If frame needs dynamic alignment push padding.
633    Label no_padding;
634    __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
635           Immediate(0));
636    __ j(equal, &no_padding);
637    __ push(Immediate(kAlignmentZapValue));
638    __ bind(&no_padding);
639  }
640
641  // Replace the current frame with the output frames.
642  Label outer_push_loop, inner_push_loop,
643      outer_loop_header, inner_loop_header;
644  // Outer loop state: eax = current FrameDescription**, edx = one past the
645  // last FrameDescription**.
646  __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
647  __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
648  __ lea(edx, Operand(eax, edx, times_4, 0));
649  __ jmp(&outer_loop_header);
650  __ bind(&outer_push_loop);
651  // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
652  __ mov(ebx, Operand(eax, 0));
653  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
654  __ jmp(&inner_loop_header);
655  __ bind(&inner_push_loop);
656  __ sub(ecx, Immediate(sizeof(uint32_t)));
657  __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset()));
658  __ bind(&inner_loop_header);
659  __ test(ecx, ecx);
660  __ j(not_zero, &inner_push_loop);
661  __ add(eax, Immediate(kPointerSize));
662  __ bind(&outer_loop_header);
663  __ cmp(eax, edx);
664  __ j(below, &outer_push_loop);
665
666  // In case of OSR or a failed STUB, we have to restore the XMM registers.
667  if (CpuFeatures::IsSupported(SSE2)) {
668    CpuFeatureScope scope(masm(), SSE2);
669    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
670      XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
671      int src_offset = i * kDoubleSize + double_regs_offset;
672      __ movdbl(xmm_reg, Operand(ebx, src_offset));
673    }
674  }
675
676  // Push state, pc, and continuation from the last output frame.
677  if (type() != OSR) {
678    __ push(Operand(ebx, FrameDescription::state_offset()));
679  }
680  __ push(Operand(ebx, FrameDescription::pc_offset()));
681  __ push(Operand(ebx, FrameDescription::continuation_offset()));
682
683
684  // Push the registers from the last output frame.
685  for (int i = 0; i < kNumberOfRegisters; i++) {
686    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
687    __ push(Operand(ebx, offset));
688  }
689
690  // Restore the registers from the stack.
691  __ popad();
692
693  // Return to the continuation point.
694  __ ret(0);
695}
696
697
698void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
699  // Create a sequence of deoptimization entries.
700  Label done;
701  for (int i = 0; i < count(); i++) {
702    int start = masm()->pc_offset();
703    USE(start);
704    __ push_imm32(i);
705    __ jmp(&done);
706    ASSERT(masm()->pc_offset() - start == table_entry_size_);
707  }
708  __ bind(&done);
709}
710
711
712void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
713  SetFrameSlot(offset, value);
714}
715
716
717void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
718  SetFrameSlot(offset, value);
719}
720
721
722#undef __
723
724
725} }  // namespace v8::internal
726
727#endif  // V8_TARGET_ARCH_IA32
728