Home | History | Annotate | Download | only in x64
      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_X64
     31 
     32 #include "codegen.h"
     33 #include "deoptimizer.h"
     34 #include "full-codegen.h"
     35 #include "safepoint-table.h"
     36 
     37 namespace v8 {
     38 namespace internal {
     39 
     40 
     41 const int Deoptimizer::table_entry_size_ = 10;
     42 
     43 
     44 int Deoptimizer::patch_size() {
     45   return Assembler::kCallSequenceLength;
     46 }
     47 
     48 
     49 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
     50   // Invalidate the relocation information, as it will become invalid by the
     51   // code patching below, and is not needed any more.
     52   code->InvalidateRelocation();
     53 
     54   // For each LLazyBailout instruction insert a absolute call to the
     55   // corresponding deoptimization entry, or a short call to an absolute
     56   // jump if space is short. The absolute jumps are put in a table just
     57   // before the safepoint table (space was allocated there when the Code
     58   // object was created, if necessary).
     59 
     60   Address instruction_start = code->instruction_start();
     61 #ifdef DEBUG
     62   Address prev_call_address = NULL;
     63 #endif
     64   DeoptimizationInputData* deopt_data =
     65       DeoptimizationInputData::cast(code->deoptimization_data());
     66   for (int i = 0; i < deopt_data->DeoptCount(); i++) {
     67     if (deopt_data->Pc(i)->value() == -1) continue;
     68     // Position where Call will be patched in.
     69     Address call_address = instruction_start + deopt_data->Pc(i)->value();
     70     // There is room enough to write a long call instruction because we pad
     71     // LLazyBailout instructions with nops if necessary.
     72     CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
     73     patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
     74                          RelocInfo::NONE64);
     75     ASSERT(prev_call_address == NULL ||
     76            call_address >= prev_call_address + patch_size());
     77     ASSERT(call_address + patch_size() <= code->instruction_end());
     78 #ifdef DEBUG
     79     prev_call_address = call_address;
     80 #endif
     81   }
     82 }
     83 
     84 
     85 static const byte kJnsInstruction = 0x79;
     86 static const byte kJnsOffset = 0x1d;
     87 static const byte kCallInstruction = 0xe8;
     88 static const byte kNopByteOne = 0x66;
     89 static const byte kNopByteTwo = 0x90;
     90 
     91 // The back edge bookkeeping code matches the pattern:
     92 //
     93 //     add <profiling_counter>, <-delta>
     94 //     jns ok
     95 //     call <stack guard>
     96 //   ok:
     97 //
     98 // We will patch away the branch so the code is:
     99 //
    100 //     add <profiling_counter>, <-delta>  ;; Not changed
    101 //     nop
    102 //     nop
    103 //     call <on-stack replacment>
    104 //   ok:
    105 
    106 void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
    107                                        Address pc_after,
    108                                        Code* interrupt_code,
    109                                        Code* replacement_code) {
    110   ASSERT(!InterruptCodeIsPatched(unoptimized_code,
    111                                  pc_after,
    112                                  interrupt_code,
    113                                  replacement_code));
    114   // Turn the jump into nops.
    115   Address call_target_address = pc_after - kIntSize;
    116   *(call_target_address - 3) = kNopByteOne;
    117   *(call_target_address - 2) = kNopByteTwo;
    118   // Replace the call address.
    119   Assembler::set_target_address_at(call_target_address,
    120                                    replacement_code->entry());
    121 
    122   unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
    123       unoptimized_code, call_target_address, replacement_code);
    124 }
    125 
    126 
    127 void Deoptimizer::RevertInterruptCodeAt(Code* unoptimized_code,
    128                                         Address pc_after,
    129                                         Code* interrupt_code,
    130                                         Code* replacement_code) {
    131   ASSERT(InterruptCodeIsPatched(unoptimized_code,
    132                                 pc_after,
    133                                 interrupt_code,
    134                                 replacement_code));
    135   // Restore the original jump.
    136   Address call_target_address = pc_after - kIntSize;
    137   *(call_target_address - 3) = kJnsInstruction;
    138   *(call_target_address - 2) = kJnsOffset;
    139   // Restore the original call address.
    140   Assembler::set_target_address_at(call_target_address,
    141                                    interrupt_code->entry());
    142 
    143   interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
    144       unoptimized_code, call_target_address, interrupt_code);
    145 }
    146 
    147 
    148 #ifdef DEBUG
    149 bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
    150                                          Address pc_after,
    151                                          Code* interrupt_code,
    152                                          Code* replacement_code) {
    153   Address call_target_address = pc_after - kIntSize;
    154   ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
    155   if (*(call_target_address - 3) == kNopByteOne) {
    156     ASSERT(replacement_code->entry() ==
    157            Assembler::target_address_at(call_target_address));
    158     ASSERT_EQ(kNopByteTwo,      *(call_target_address - 2));
    159     return true;
    160   } else {
    161     ASSERT_EQ(interrupt_code->entry(),
    162               Assembler::target_address_at(call_target_address));
    163     ASSERT_EQ(kJnsInstruction,  *(call_target_address - 3));
    164     ASSERT_EQ(kJnsOffset,       *(call_target_address - 2));
    165     return false;
    166   }
    167 }
    168 #endif  // DEBUG
    169 
    170 
    171 static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
    172   ByteArray* translations = data->TranslationByteArray();
    173   int length = data->DeoptCount();
    174   for (int i = 0; i < length; i++) {
    175     if (data->AstId(i) == ast_id) {
    176       TranslationIterator it(translations,  data->TranslationIndex(i)->value());
    177       int value = it.Next();
    178       ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
    179       // Read the number of frames.
    180       value = it.Next();
    181       if (value == 1) return i;
    182     }
    183   }
    184   UNREACHABLE();
    185   return -1;
    186 }
    187 
    188 
    189 void Deoptimizer::DoComputeOsrOutputFrame() {
    190   DeoptimizationInputData* data = DeoptimizationInputData::cast(
    191       compiled_code_->deoptimization_data());
    192   unsigned ast_id = data->OsrAstId()->value();
    193   // TODO(kasperl): This should not be the bailout_id_. It should be
    194   // the ast id. Confusing.
    195   ASSERT(bailout_id_ == ast_id);
    196 
    197   int bailout_id = LookupBailoutId(data, BailoutId(ast_id));
    198   unsigned translation_index = data->TranslationIndex(bailout_id)->value();
    199   ByteArray* translations = data->TranslationByteArray();
    200 
    201   TranslationIterator iterator(translations, translation_index);
    202   Translation::Opcode opcode =
    203       static_cast<Translation::Opcode>(iterator.Next());
    204   ASSERT(Translation::BEGIN == opcode);
    205   USE(opcode);
    206   int count = iterator.Next();
    207   iterator.Skip(1);  // Drop JS frame count.
    208   ASSERT(count == 1);
    209   USE(count);
    210 
    211   opcode = static_cast<Translation::Opcode>(iterator.Next());
    212   USE(opcode);
    213   ASSERT(Translation::JS_FRAME == opcode);
    214   unsigned node_id = iterator.Next();
    215   USE(node_id);
    216   ASSERT(node_id == ast_id);
    217   int closure_id = iterator.Next();
    218   USE(closure_id);
    219   ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
    220   unsigned height = iterator.Next();
    221   unsigned height_in_bytes = height * kPointerSize;
    222   USE(height_in_bytes);
    223 
    224   unsigned fixed_size = ComputeFixedSize(function_);
    225   unsigned input_frame_size = input_->GetFrameSize();
    226   ASSERT(fixed_size + height_in_bytes == input_frame_size);
    227 
    228   unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize;
    229   unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
    230   unsigned outgoing_size = outgoing_height * kPointerSize;
    231   unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
    232   ASSERT(outgoing_size == 0);  // OSR does not happen in the middle of a call.
    233 
    234   if (FLAG_trace_osr) {
    235     PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
    236            reinterpret_cast<intptr_t>(function_));
    237     PrintFunctionName();
    238     PrintF(" => node=%u, frame=%d->%d]\n",
    239            ast_id,
    240            input_frame_size,
    241            output_frame_size);
    242   }
    243 
    244   // There's only one output frame in the OSR case.
    245   output_count_ = 1;
    246   output_ = new FrameDescription*[1];
    247   output_[0] = new(output_frame_size) FrameDescription(
    248       output_frame_size, function_);
    249   output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
    250 
    251   // Clear the incoming parameters in the optimized frame to avoid
    252   // confusing the garbage collector.
    253   unsigned output_offset = output_frame_size - kPointerSize;
    254   int parameter_count = function_->shared()->formal_parameter_count() + 1;
    255   for (int i = 0; i < parameter_count; ++i) {
    256     output_[0]->SetFrameSlot(output_offset, 0);
    257     output_offset -= kPointerSize;
    258   }
    259 
    260   // Translate the incoming parameters. This may overwrite some of the
    261   // incoming argument slots we've just cleared.
    262   int input_offset = input_frame_size - kPointerSize;
    263   bool ok = true;
    264   int limit = input_offset - (parameter_count * kPointerSize);
    265   while (ok && input_offset > limit) {
    266     ok = DoOsrTranslateCommand(&iterator, &input_offset);
    267   }
    268 
    269   // There are no translation commands for the caller's pc and fp, the
    270   // context, and the function.  Set them up explicitly.
    271   for (int i = StandardFrameConstants::kCallerPCOffset;
    272        ok && i >=  StandardFrameConstants::kMarkerOffset;
    273        i -= kPointerSize) {
    274     intptr_t input_value = input_->GetFrameSlot(input_offset);
    275     if (FLAG_trace_osr) {
    276       const char* name = "UNKNOWN";
    277       switch (i) {
    278         case StandardFrameConstants::kCallerPCOffset:
    279           name = "caller's pc";
    280           break;
    281         case StandardFrameConstants::kCallerFPOffset:
    282           name = "fp";
    283           break;
    284         case StandardFrameConstants::kContextOffset:
    285           name = "context";
    286           break;
    287         case StandardFrameConstants::kMarkerOffset:
    288           name = "function";
    289           break;
    290       }
    291       PrintF("    [rsp + %d] <- 0x%08" V8PRIxPTR " ; [rsp + %d] "
    292              "(fixed part - %s)\n",
    293              output_offset,
    294              input_value,
    295              input_offset,
    296              name);
    297     }
    298     output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
    299     input_offset -= kPointerSize;
    300     output_offset -= kPointerSize;
    301   }
    302 
    303   // Translate the rest of the frame.
    304   while (ok && input_offset >= 0) {
    305     ok = DoOsrTranslateCommand(&iterator, &input_offset);
    306   }
    307 
    308   // If translation of any command failed, continue using the input frame.
    309   if (!ok) {
    310     delete output_[0];
    311     output_[0] = input_;
    312     output_[0]->SetPc(reinterpret_cast<intptr_t>(from_));
    313   } else {
    314     // Set up the frame pointer and the context pointer.
    315     output_[0]->SetRegister(rbp.code(), input_->GetRegister(rbp.code()));
    316     output_[0]->SetRegister(rsi.code(), input_->GetRegister(rsi.code()));
    317 
    318     unsigned pc_offset = data->OsrPcOffset()->value();
    319     intptr_t pc = reinterpret_cast<intptr_t>(
    320         compiled_code_->entry() + pc_offset);
    321     output_[0]->SetPc(pc);
    322   }
    323   Code* continuation =
    324       function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
    325   output_[0]->SetContinuation(
    326       reinterpret_cast<intptr_t>(continuation->entry()));
    327 
    328   if (FLAG_trace_osr) {
    329     PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
    330            ok ? "finished" : "aborted",
    331            reinterpret_cast<intptr_t>(function_));
    332     PrintFunctionName();
    333     PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc());
    334   }
    335 }
    336 
    337 
    338 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
    339   // Set the register values. The values are not important as there are no
    340   // callee saved registers in JavaScript frames, so all registers are
    341   // spilled. Registers rbp and rsp are set to the correct values though.
    342   for (int i = 0; i < Register::kNumRegisters; i++) {
    343     input_->SetRegister(i, i * 4);
    344   }
    345   input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
    346   input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
    347   for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
    348     input_->SetDoubleRegister(i, 0.0);
    349   }
    350 
    351   // Fill the frame content from the actual data on the frame.
    352   for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
    353     input_->SetFrameSlot(i, Memory::uint64_at(tos + i));
    354   }
    355 }
    356 
    357 
    358 void Deoptimizer::SetPlatformCompiledStubRegisters(
    359     FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
    360   intptr_t handler =
    361       reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
    362   int params = descriptor->register_param_count_;
    363   if (descriptor->stack_parameter_count_ != NULL) {
    364     params++;
    365   }
    366   output_frame->SetRegister(rax.code(), params);
    367   output_frame->SetRegister(rbx.code(), handler);
    368 }
    369 
    370 
    371 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
    372   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    373     double double_value = input_->GetDoubleRegister(i);
    374     output_frame->SetDoubleRegister(i, double_value);
    375   }
    376 }
    377 
    378 
    379 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
    380   // There is no dynamic alignment padding on x64 in the input frame.
    381   return false;
    382 }
    383 
    384 
    385 #define __ masm()->
    386 
    387 void Deoptimizer::EntryGenerator::Generate() {
    388   GeneratePrologue();
    389 
    390   // Save all general purpose registers before messing with them.
    391   const int kNumberOfRegisters = Register::kNumRegisters;
    392 
    393   const int kDoubleRegsSize = kDoubleSize *
    394       XMMRegister::NumAllocatableRegisters();
    395   __ subq(rsp, Immediate(kDoubleRegsSize));
    396 
    397   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    398     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    399     int offset = i * kDoubleSize;
    400     __ movsd(Operand(rsp, offset), xmm_reg);
    401   }
    402 
    403   // We push all registers onto the stack, even though we do not need
    404   // to restore all later.
    405   for (int i = 0; i < kNumberOfRegisters; i++) {
    406     Register r = Register::from_code(i);
    407     __ push(r);
    408   }
    409 
    410   const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
    411                                       kDoubleRegsSize;
    412 
    413   // We use this to keep the value of the fifth argument temporarily.
    414   // Unfortunately we can't store it directly in r8 (used for passing
    415   // this on linux), since it is another parameter passing register on windows.
    416   Register arg5 = r11;
    417 
    418   // Get the bailout id from the stack.
    419   __ movq(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
    420 
    421   // Get the address of the location in the code object
    422   // and compute the fp-to-sp delta in register arg5.
    423   __ movq(arg_reg_4,
    424           Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
    425   __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 2 * kPointerSize));
    426 
    427   __ subq(arg5, rbp);
    428   __ neg(arg5);
    429 
    430   // Allocate a new deoptimizer object.
    431   __ PrepareCallCFunction(6);
    432   __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
    433   __ movq(arg_reg_1, rax);
    434   __ Set(arg_reg_2, type());
    435   // Args 3 and 4 are already in the right registers.
    436 
    437   // On windows put the arguments on the stack (PrepareCallCFunction
    438   // has created space for this). On linux pass the arguments in r8 and r9.
    439 #ifdef _WIN64
    440   __ movq(Operand(rsp, 4 * kPointerSize), arg5);
    441   __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
    442   __ movq(Operand(rsp, 5 * kPointerSize), arg5);
    443 #else
    444   __ movq(r8, arg5);
    445   __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
    446 #endif
    447 
    448   { AllowExternalCallThatCantCauseGC scope(masm());
    449     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
    450   }
    451   // Preserve deoptimizer object in register rax and get the input
    452   // frame descriptor pointer.
    453   __ movq(rbx, Operand(rax, Deoptimizer::input_offset()));
    454 
    455   // Fill in the input registers.
    456   for (int i = kNumberOfRegisters -1; i >= 0; i--) {
    457     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    458     __ pop(Operand(rbx, offset));
    459   }
    460 
    461   // Fill in the double input registers.
    462   int double_regs_offset = FrameDescription::double_registers_offset();
    463   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
    464     int dst_offset = i * kDoubleSize + double_regs_offset;
    465     __ pop(Operand(rbx, dst_offset));
    466   }
    467 
    468   // Remove the bailout id and return address from the stack.
    469   __ addq(rsp, Immediate(2 * kPointerSize));
    470 
    471   // Compute a pointer to the unwinding limit in register rcx; that is
    472   // the first stack slot not part of the input frame.
    473   __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
    474   __ addq(rcx, rsp);
    475 
    476   // Unwind the stack down to - but not including - the unwinding
    477   // limit and copy the contents of the activation frame to the input
    478   // frame description.
    479   __ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
    480   Label pop_loop_header;
    481   __ jmp(&pop_loop_header);
    482   Label pop_loop;
    483   __ bind(&pop_loop);
    484   __ pop(Operand(rdx, 0));
    485   __ addq(rdx, Immediate(sizeof(intptr_t)));
    486   __ bind(&pop_loop_header);
    487   __ cmpq(rcx, rsp);
    488   __ j(not_equal, &pop_loop);
    489 
    490   // Compute the output frame in the deoptimizer.
    491   __ push(rax);
    492   __ PrepareCallCFunction(2);
    493   __ movq(arg_reg_1, rax);
    494   __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
    495   {
    496     AllowExternalCallThatCantCauseGC scope(masm());
    497     __ CallCFunction(
    498         ExternalReference::compute_output_frames_function(isolate()), 2);
    499   }
    500   __ pop(rax);
    501 
    502   // Replace the current frame with the output frames.
    503   Label outer_push_loop, inner_push_loop,
    504       outer_loop_header, inner_loop_header;
    505   // Outer loop state: rax = current FrameDescription**, rdx = one past the
    506   // last FrameDescription**.
    507   __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
    508   __ movq(rax, Operand(rax, Deoptimizer::output_offset()));
    509   __ lea(rdx, Operand(rax, rdx, times_pointer_size, 0));
    510   __ jmp(&outer_loop_header);
    511   __ bind(&outer_push_loop);
    512   // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
    513   __ movq(rbx, Operand(rax, 0));
    514   __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
    515   __ jmp(&inner_loop_header);
    516   __ bind(&inner_push_loop);
    517   __ subq(rcx, Immediate(sizeof(intptr_t)));
    518   __ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
    519   __ bind(&inner_loop_header);
    520   __ testq(rcx, rcx);
    521   __ j(not_zero, &inner_push_loop);
    522   __ addq(rax, Immediate(kPointerSize));
    523   __ bind(&outer_loop_header);
    524   __ cmpq(rax, rdx);
    525   __ j(below, &outer_push_loop);
    526 
    527   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    528     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    529     int src_offset = i * kDoubleSize + double_regs_offset;
    530     __ movsd(xmm_reg, Operand(rbx, src_offset));
    531   }
    532 
    533   // Push state, pc, and continuation from the last output frame.
    534   if (type() != OSR) {
    535     __ push(Operand(rbx, FrameDescription::state_offset()));
    536   }
    537   __ push(Operand(rbx, FrameDescription::pc_offset()));
    538   __ push(Operand(rbx, FrameDescription::continuation_offset()));
    539 
    540   // Push the registers from the last output frame.
    541   for (int i = 0; i < kNumberOfRegisters; i++) {
    542     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    543     __ push(Operand(rbx, offset));
    544   }
    545 
    546   // Restore the registers from the stack.
    547   for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
    548     Register r = Register::from_code(i);
    549     // Do not restore rsp, simply pop the value into the next register
    550     // and overwrite this afterwards.
    551     if (r.is(rsp)) {
    552       ASSERT(i > 0);
    553       r = Register::from_code(i - 1);
    554     }
    555     __ pop(r);
    556   }
    557 
    558   // Set up the roots register.
    559   __ InitializeRootRegister();
    560   __ InitializeSmiConstantRegister();
    561 
    562   // Return to the continuation point.
    563   __ ret(0);
    564 }
    565 
    566 
    567 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
    568   // Create a sequence of deoptimization entries.
    569   Label done;
    570   for (int i = 0; i < count(); i++) {
    571     int start = masm()->pc_offset();
    572     USE(start);
    573     __ push_imm32(i);
    574     __ jmp(&done);
    575     ASSERT(masm()->pc_offset() - start == table_entry_size_);
    576   }
    577   __ bind(&done);
    578 }
    579 
    580 
    581 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
    582   SetFrameSlot(offset, value);
    583 }
    584 
    585 
    586 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
    587   SetFrameSlot(offset, value);
    588 }
    589 
    590 
    591 #undef __
    592 
    593 
    594 } }  // namespace v8::internal
    595 
    596 #endif  // V8_TARGET_ARCH_X64
    597