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