Home | History | Annotate | Download | only in x64
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/v8.h"
      6 
      7 #if V8_TARGET_ARCH_X64
      8 
      9 #include "src/codegen.h"
     10 #include "src/deoptimizer.h"
     11 #include "src/full-codegen.h"
     12 #include "src/safepoint-table.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 
     18 const int Deoptimizer::table_entry_size_ = 10;
     19 
     20 
     21 int Deoptimizer::patch_size() {
     22   return Assembler::kCallSequenceLength;
     23 }
     24 
     25 
     26 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
     27   // Invalidate the relocation information, as it will become invalid by the
     28   // code patching below, and is not needed any more.
     29   code->InvalidateRelocation();
     30 
     31   if (FLAG_zap_code_space) {
     32     // Fail hard and early if we enter this code object again.
     33     byte* pointer = code->FindCodeAgeSequence();
     34     if (pointer != NULL) {
     35       pointer += kNoCodeAgeSequenceLength;
     36     } else {
     37       pointer = code->instruction_start();
     38     }
     39     CodePatcher patcher(pointer, 1);
     40     patcher.masm()->int3();
     41 
     42     DeoptimizationInputData* data =
     43         DeoptimizationInputData::cast(code->deoptimization_data());
     44     int osr_offset = data->OsrPcOffset()->value();
     45     if (osr_offset > 0) {
     46       CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
     47       osr_patcher.masm()->int3();
     48     }
     49   }
     50 
     51   // For each LLazyBailout instruction insert a absolute call to the
     52   // corresponding deoptimization entry, or a short call to an absolute
     53   // jump if space is short. The absolute jumps are put in a table just
     54   // before the safepoint table (space was allocated there when the Code
     55   // object was created, if necessary).
     56 
     57   Address instruction_start = code->instruction_start();
     58 #ifdef DEBUG
     59   Address prev_call_address = NULL;
     60 #endif
     61   DeoptimizationInputData* deopt_data =
     62       DeoptimizationInputData::cast(code->deoptimization_data());
     63   SharedFunctionInfo* shared =
     64       SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
     65   shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
     66   deopt_data->SetSharedFunctionInfo(Smi::FromInt(0));
     67   // For each LLazyBailout instruction insert a call to the corresponding
     68   // deoptimization entry.
     69   for (int i = 0; i < deopt_data->DeoptCount(); i++) {
     70     if (deopt_data->Pc(i)->value() == -1) continue;
     71     // Position where Call will be patched in.
     72     Address call_address = instruction_start + deopt_data->Pc(i)->value();
     73     // There is room enough to write a long call instruction because we pad
     74     // LLazyBailout instructions with nops if necessary.
     75     CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
     76     patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
     77                          Assembler::RelocInfoNone());
     78     ASSERT(prev_call_address == NULL ||
     79            call_address >= prev_call_address + patch_size());
     80     ASSERT(call_address + patch_size() <= code->instruction_end());
     81 #ifdef DEBUG
     82     prev_call_address = call_address;
     83 #endif
     84   }
     85 }
     86 
     87 
     88 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
     89   // Set the register values. The values are not important as there are no
     90   // callee saved registers in JavaScript frames, so all registers are
     91   // spilled. Registers rbp and rsp are set to the correct values though.
     92   for (int i = 0; i < Register::kNumRegisters; i++) {
     93     input_->SetRegister(i, i * 4);
     94   }
     95   input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
     96   input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
     97   for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
     98     input_->SetDoubleRegister(i, 0.0);
     99   }
    100 
    101   // Fill the frame content from the actual data on the frame.
    102   for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
    103     input_->SetFrameSlot(i, Memory::uintptr_at(tos + i));
    104   }
    105 }
    106 
    107 
    108 void Deoptimizer::SetPlatformCompiledStubRegisters(
    109     FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
    110   intptr_t handler =
    111       reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
    112   int params = descriptor->GetHandlerParameterCount();
    113   output_frame->SetRegister(rax.code(), params);
    114   output_frame->SetRegister(rbx.code(), handler);
    115 }
    116 
    117 
    118 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
    119   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    120     double double_value = input_->GetDoubleRegister(i);
    121     output_frame->SetDoubleRegister(i, double_value);
    122   }
    123 }
    124 
    125 
    126 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
    127   // There is no dynamic alignment padding on x64 in the input frame.
    128   return false;
    129 }
    130 
    131 
    132 #define __ masm()->
    133 
    134 void Deoptimizer::EntryGenerator::Generate() {
    135   GeneratePrologue();
    136 
    137   // Save all general purpose registers before messing with them.
    138   const int kNumberOfRegisters = Register::kNumRegisters;
    139 
    140   const int kDoubleRegsSize = kDoubleSize *
    141       XMMRegister::NumAllocatableRegisters();
    142   __ subp(rsp, Immediate(kDoubleRegsSize));
    143 
    144   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    145     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    146     int offset = i * kDoubleSize;
    147     __ movsd(Operand(rsp, offset), xmm_reg);
    148   }
    149 
    150   // We push all registers onto the stack, even though we do not need
    151   // to restore all later.
    152   for (int i = 0; i < kNumberOfRegisters; i++) {
    153     Register r = Register::from_code(i);
    154     __ pushq(r);
    155   }
    156 
    157   const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize +
    158                                       kDoubleRegsSize;
    159 
    160   // We use this to keep the value of the fifth argument temporarily.
    161   // Unfortunately we can't store it directly in r8 (used for passing
    162   // this on linux), since it is another parameter passing register on windows.
    163   Register arg5 = r11;
    164 
    165   // Get the bailout id from the stack.
    166   __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
    167 
    168   // Get the address of the location in the code object
    169   // and compute the fp-to-sp delta in register arg5.
    170   __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize));
    171   __ leap(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize +
    172                             kPCOnStackSize));
    173 
    174   __ subp(arg5, rbp);
    175   __ negp(arg5);
    176 
    177   // Allocate a new deoptimizer object.
    178   __ PrepareCallCFunction(6);
    179   __ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
    180   __ movp(arg_reg_1, rax);
    181   __ Set(arg_reg_2, type());
    182   // Args 3 and 4 are already in the right registers.
    183 
    184   // On windows put the arguments on the stack (PrepareCallCFunction
    185   // has created space for this). On linux pass the arguments in r8 and r9.
    186 #ifdef _WIN64
    187   __ movq(Operand(rsp, 4 * kRegisterSize), arg5);
    188   __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
    189   __ movq(Operand(rsp, 5 * kRegisterSize), arg5);
    190 #else
    191   __ movp(r8, arg5);
    192   __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
    193 #endif
    194 
    195   { AllowExternalCallThatCantCauseGC scope(masm());
    196     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
    197   }
    198   // Preserve deoptimizer object in register rax and get the input
    199   // frame descriptor pointer.
    200   __ movp(rbx, Operand(rax, Deoptimizer::input_offset()));
    201 
    202   // Fill in the input registers.
    203   for (int i = kNumberOfRegisters -1; i >= 0; i--) {
    204     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    205     __ PopQuad(Operand(rbx, offset));
    206   }
    207 
    208   // Fill in the double input registers.
    209   int double_regs_offset = FrameDescription::double_registers_offset();
    210   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
    211     int dst_offset = i * kDoubleSize + double_regs_offset;
    212     __ popq(Operand(rbx, dst_offset));
    213   }
    214 
    215   // Remove the bailout id and return address from the stack.
    216   __ addp(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize));
    217 
    218   // Compute a pointer to the unwinding limit in register rcx; that is
    219   // the first stack slot not part of the input frame.
    220   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
    221   __ addp(rcx, rsp);
    222 
    223   // Unwind the stack down to - but not including - the unwinding
    224   // limit and copy the contents of the activation frame to the input
    225   // frame description.
    226   __ leap(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
    227   Label pop_loop_header;
    228   __ jmp(&pop_loop_header);
    229   Label pop_loop;
    230   __ bind(&pop_loop);
    231   __ Pop(Operand(rdx, 0));
    232   __ addp(rdx, Immediate(sizeof(intptr_t)));
    233   __ bind(&pop_loop_header);
    234   __ cmpp(rcx, rsp);
    235   __ j(not_equal, &pop_loop);
    236 
    237   // Compute the output frame in the deoptimizer.
    238   __ pushq(rax);
    239   __ PrepareCallCFunction(2);
    240   __ movp(arg_reg_1, rax);
    241   __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
    242   {
    243     AllowExternalCallThatCantCauseGC scope(masm());
    244     __ CallCFunction(
    245         ExternalReference::compute_output_frames_function(isolate()), 2);
    246   }
    247   __ popq(rax);
    248 
    249   // Replace the current frame with the output frames.
    250   Label outer_push_loop, inner_push_loop,
    251       outer_loop_header, inner_loop_header;
    252   // Outer loop state: rax = current FrameDescription**, rdx = one past the
    253   // last FrameDescription**.
    254   __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
    255   __ movp(rax, Operand(rax, Deoptimizer::output_offset()));
    256   __ leap(rdx, Operand(rax, rdx, times_pointer_size, 0));
    257   __ jmp(&outer_loop_header);
    258   __ bind(&outer_push_loop);
    259   // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
    260   __ movp(rbx, Operand(rax, 0));
    261   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
    262   __ jmp(&inner_loop_header);
    263   __ bind(&inner_push_loop);
    264   __ subp(rcx, Immediate(sizeof(intptr_t)));
    265   __ Push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
    266   __ bind(&inner_loop_header);
    267   __ testp(rcx, rcx);
    268   __ j(not_zero, &inner_push_loop);
    269   __ addp(rax, Immediate(kPointerSize));
    270   __ bind(&outer_loop_header);
    271   __ cmpp(rax, rdx);
    272   __ j(below, &outer_push_loop);
    273 
    274   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
    275     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    276     int src_offset = i * kDoubleSize + double_regs_offset;
    277     __ movsd(xmm_reg, Operand(rbx, src_offset));
    278   }
    279 
    280   // Push state, pc, and continuation from the last output frame.
    281   __ Push(Operand(rbx, FrameDescription::state_offset()));
    282   __ PushQuad(Operand(rbx, FrameDescription::pc_offset()));
    283   __ PushQuad(Operand(rbx, FrameDescription::continuation_offset()));
    284 
    285   // Push the registers from the last output frame.
    286   for (int i = 0; i < kNumberOfRegisters; i++) {
    287     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    288     __ PushQuad(Operand(rbx, offset));
    289   }
    290 
    291   // Restore the registers from the stack.
    292   for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
    293     Register r = Register::from_code(i);
    294     // Do not restore rsp, simply pop the value into the next register
    295     // and overwrite this afterwards.
    296     if (r.is(rsp)) {
    297       ASSERT(i > 0);
    298       r = Register::from_code(i - 1);
    299     }
    300     __ popq(r);
    301   }
    302 
    303   // Set up the roots register.
    304   __ InitializeRootRegister();
    305   __ InitializeSmiConstantRegister();
    306 
    307   // Return to the continuation point.
    308   __ ret(0);
    309 }
    310 
    311 
    312 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
    313   // Create a sequence of deoptimization entries.
    314   Label done;
    315   for (int i = 0; i < count(); i++) {
    316     int start = masm()->pc_offset();
    317     USE(start);
    318     __ pushq_imm32(i);
    319     __ jmp(&done);
    320     ASSERT(masm()->pc_offset() - start == table_entry_size_);
    321   }
    322   __ bind(&done);
    323 }
    324 
    325 
    326 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
    327   if (kPCOnStackSize == 2 * kPointerSize) {
    328     // Zero out the high-32 bit of PC for x32 port.
    329     SetFrameSlot(offset + kPointerSize, 0);
    330   }
    331   SetFrameSlot(offset, value);
    332 }
    333 
    334 
    335 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
    336   if (kFPOnStackSize == 2 * kPointerSize) {
    337     // Zero out the high-32 bit of FP for x32 port.
    338     SetFrameSlot(offset + kPointerSize, 0);
    339   }
    340   SetFrameSlot(offset, value);
    341 }
    342 
    343 
    344 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
    345   // No out-of-line constant pool support.
    346   UNREACHABLE();
    347 }
    348 
    349 
    350 #undef __
    351 
    352 
    353 } }  // namespace v8::internal
    354 
    355 #endif  // V8_TARGET_ARCH_X64
    356