Home | History | Annotate | Download | only in mips
      1 
      2 // Copyright 2011 the V8 project authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 
      6 #include "src/v8.h"
      7 
      8 #include "src/codegen.h"
      9 #include "src/deoptimizer.h"
     10 #include "src/full-codegen.h"
     11 #include "src/safepoint-table.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 
     17 int Deoptimizer::patch_size() {
     18   const int kCallInstructionSizeInWords = 4;
     19   return kCallInstructionSizeInWords * Assembler::kInstrSize;
     20 }
     21 
     22 
     23 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
     24   Address code_start_address = code->instruction_start();
     25   // Invalidate the relocation information, as it will become invalid by the
     26   // code patching below, and is not needed any more.
     27   code->InvalidateRelocation();
     28 
     29   if (FLAG_zap_code_space) {
     30     // Fail hard and early if we enter this code object again.
     31     byte* pointer = code->FindCodeAgeSequence();
     32     if (pointer != NULL) {
     33       pointer += kNoCodeAgeSequenceLength;
     34     } else {
     35       pointer = code->instruction_start();
     36     }
     37     CodePatcher patcher(pointer, 1);
     38     patcher.masm()->break_(0xCC);
     39 
     40     DeoptimizationInputData* data =
     41         DeoptimizationInputData::cast(code->deoptimization_data());
     42     int osr_offset = data->OsrPcOffset()->value();
     43     if (osr_offset > 0) {
     44       CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
     45       osr_patcher.masm()->break_(0xCC);
     46     }
     47   }
     48 
     49   DeoptimizationInputData* deopt_data =
     50       DeoptimizationInputData::cast(code->deoptimization_data());
     51   SharedFunctionInfo* shared =
     52       SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
     53   shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
     54 #ifdef DEBUG
     55   Address prev_call_address = NULL;
     56 #endif
     57   // For each LLazyBailout instruction insert a call to the corresponding
     58   // deoptimization entry.
     59   for (int i = 0; i < deopt_data->DeoptCount(); i++) {
     60     if (deopt_data->Pc(i)->value() == -1) continue;
     61     Address call_address = code_start_address + deopt_data->Pc(i)->value();
     62     Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
     63     int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
     64                                                       RelocInfo::NONE32);
     65     int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
     66     ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0);
     67     ASSERT(call_size_in_bytes <= patch_size());
     68     CodePatcher patcher(call_address, call_size_in_words);
     69     patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
     70     ASSERT(prev_call_address == NULL ||
     71            call_address >= prev_call_address + patch_size());
     72     ASSERT(call_address + patch_size() <= code->instruction_end());
     73 
     74 #ifdef DEBUG
     75     prev_call_address = call_address;
     76 #endif
     77   }
     78 }
     79 
     80 
     81 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
     82   // Set the register values. The values are not important as there are no
     83   // callee saved registers in JavaScript frames, so all registers are
     84   // spilled. Registers fp and sp are set to the correct values though.
     85 
     86   for (int i = 0; i < Register::kNumRegisters; i++) {
     87     input_->SetRegister(i, i * 4);
     88   }
     89   input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
     90   input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
     91   for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
     92     input_->SetDoubleRegister(i, 0.0);
     93   }
     94 
     95   // Fill the frame content from the actual data on the frame.
     96   for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
     97     input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
     98   }
     99 }
    100 
    101 
    102 void Deoptimizer::SetPlatformCompiledStubRegisters(
    103     FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
    104   ApiFunction function(descriptor->deoptimization_handler_);
    105   ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
    106   intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
    107   int params = descriptor->GetHandlerParameterCount();
    108   output_frame->SetRegister(s0.code(), params);
    109   output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize);
    110   output_frame->SetRegister(s2.code(), handler);
    111 }
    112 
    113 
    114 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
    115   for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
    116     double double_value = input_->GetDoubleRegister(i);
    117     output_frame->SetDoubleRegister(i, double_value);
    118   }
    119 }
    120 
    121 
    122 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
    123   // There is no dynamic alignment padding on MIPS in the input frame.
    124   return false;
    125 }
    126 
    127 
    128 #define __ masm()->
    129 
    130 
    131 // This code tries to be close to ia32 code so that any changes can be
    132 // easily ported.
    133 void Deoptimizer::EntryGenerator::Generate() {
    134   GeneratePrologue();
    135 
    136   // Unlike on ARM we don't save all the registers, just the useful ones.
    137   // For the rest, there are gaps on the stack, so the offsets remain the same.
    138   const int kNumberOfRegisters = Register::kNumRegisters;
    139 
    140   RegList restored_regs = kJSCallerSaved | kCalleeSaved;
    141   RegList saved_regs = restored_regs | sp.bit() | ra.bit();
    142 
    143   const int kDoubleRegsSize =
    144       kDoubleSize * FPURegister::kMaxNumAllocatableRegisters;
    145 
    146   // Save all FPU registers before messing with them.
    147   __ Subu(sp, sp, Operand(kDoubleRegsSize));
    148   for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
    149     FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
    150     int offset = i * kDoubleSize;
    151     __ sdc1(fpu_reg, MemOperand(sp, offset));
    152   }
    153 
    154   // Push saved_regs (needed to populate FrameDescription::registers_).
    155   // Leave gaps for other registers.
    156   __ Subu(sp, sp, kNumberOfRegisters * kPointerSize);
    157   for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
    158     if ((saved_regs & (1 << i)) != 0) {
    159       __ sw(ToRegister(i), MemOperand(sp, kPointerSize * i));
    160     }
    161   }
    162 
    163   const int kSavedRegistersAreaSize =
    164       (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
    165 
    166   // Get the bailout id from the stack.
    167   __ lw(a2, MemOperand(sp, kSavedRegistersAreaSize));
    168 
    169   // Get the address of the location in the code object (a3) (return
    170   // address for lazy deoptimization) and compute the fp-to-sp delta in
    171   // register t0.
    172   __ mov(a3, ra);
    173   // Correct one word for bailout id.
    174   __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
    175 
    176   __ Subu(t0, fp, t0);
    177 
    178   // Allocate a new deoptimizer object.
    179   // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
    180   __ PrepareCallCFunction(6, t1);
    181   __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
    182   __ li(a1, Operand(type()));  // bailout type,
    183   // a2: bailout id already loaded.
    184   // a3: code address or 0 already loaded.
    185   __ sw(t0, CFunctionArgumentOperand(5));  // Fp-to-sp delta.
    186   __ li(t1, Operand(ExternalReference::isolate_address(isolate())));
    187   __ sw(t1, CFunctionArgumentOperand(6));  // Isolate.
    188   // Call Deoptimizer::New().
    189   {
    190     AllowExternalCallThatCantCauseGC scope(masm());
    191     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
    192   }
    193 
    194   // Preserve "deoptimizer" object in register v0 and get the input
    195   // frame descriptor pointer to a1 (deoptimizer->input_);
    196   // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
    197   __ mov(a0, v0);
    198   __ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
    199 
    200   // Copy core registers into FrameDescription::registers_[kNumRegisters].
    201   ASSERT(Register::kNumRegisters == kNumberOfRegisters);
    202   for (int i = 0; i < kNumberOfRegisters; i++) {
    203     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    204     if ((saved_regs & (1 << i)) != 0) {
    205       __ lw(a2, MemOperand(sp, i * kPointerSize));
    206       __ sw(a2, MemOperand(a1, offset));
    207     } else if (FLAG_debug_code) {
    208       __ li(a2, kDebugZapValue);
    209       __ sw(a2, MemOperand(a1, offset));
    210     }
    211   }
    212 
    213   int double_regs_offset = FrameDescription::double_registers_offset();
    214   // Copy FPU registers to
    215   // double_registers_[DoubleRegister::kNumAllocatableRegisters]
    216   for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) {
    217     int dst_offset = i * kDoubleSize + double_regs_offset;
    218     int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
    219     __ ldc1(f0, MemOperand(sp, src_offset));
    220     __ sdc1(f0, MemOperand(a1, dst_offset));
    221   }
    222 
    223   // Remove the bailout id and the saved registers from the stack.
    224   __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
    225 
    226   // Compute a pointer to the unwinding limit in register a2; that is
    227   // the first stack slot not part of the input frame.
    228   __ lw(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
    229   __ Addu(a2, a2, sp);
    230 
    231   // Unwind the stack down to - but not including - the unwinding
    232   // limit and copy the contents of the activation frame to the input
    233   // frame description.
    234   __ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
    235   Label pop_loop;
    236   Label pop_loop_header;
    237   __ BranchShort(&pop_loop_header);
    238   __ bind(&pop_loop);
    239   __ pop(t0);
    240   __ sw(t0, MemOperand(a3, 0));
    241   __ addiu(a3, a3, sizeof(uint32_t));
    242   __ bind(&pop_loop_header);
    243   __ BranchShort(&pop_loop, ne, a2, Operand(sp));
    244 
    245   // Compute the output frame in the deoptimizer.
    246   __ push(a0);  // Preserve deoptimizer object across call.
    247   // a0: deoptimizer object; a1: scratch.
    248   __ PrepareCallCFunction(1, a1);
    249   // Call Deoptimizer::ComputeOutputFrames().
    250   {
    251     AllowExternalCallThatCantCauseGC scope(masm());
    252     __ CallCFunction(
    253         ExternalReference::compute_output_frames_function(isolate()), 1);
    254   }
    255   __ pop(a0);  // Restore deoptimizer object (class Deoptimizer).
    256 
    257   // Replace the current (input) frame with the output frames.
    258   Label outer_push_loop, inner_push_loop,
    259       outer_loop_header, inner_loop_header;
    260   // Outer loop state: t0 = current "FrameDescription** output_",
    261   // a1 = one past the last FrameDescription**.
    262   __ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
    263   __ lw(t0, MemOperand(a0, Deoptimizer::output_offset()));  // t0 is output_.
    264   __ sll(a1, a1, kPointerSizeLog2);  // Count to offset.
    265   __ addu(a1, t0, a1);  // a1 = one past the last FrameDescription**.
    266   __ jmp(&outer_loop_header);
    267   __ bind(&outer_push_loop);
    268   // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
    269   __ lw(a2, MemOperand(t0, 0));  // output_[ix]
    270   __ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
    271   __ jmp(&inner_loop_header);
    272   __ bind(&inner_push_loop);
    273   __ Subu(a3, a3, Operand(sizeof(uint32_t)));
    274   __ Addu(t2, a2, Operand(a3));
    275   __ lw(t3, MemOperand(t2, FrameDescription::frame_content_offset()));
    276   __ push(t3);
    277   __ bind(&inner_loop_header);
    278   __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
    279 
    280   __ Addu(t0, t0, Operand(kPointerSize));
    281   __ bind(&outer_loop_header);
    282   __ BranchShort(&outer_push_loop, lt, t0, Operand(a1));
    283 
    284   __ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
    285   for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
    286     const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
    287     int src_offset = i * kDoubleSize + double_regs_offset;
    288     __ ldc1(fpu_reg, MemOperand(a1, src_offset));
    289   }
    290 
    291   // Push state, pc, and continuation from the last output frame.
    292   __ lw(t2, MemOperand(a2, FrameDescription::state_offset()));
    293   __ push(t2);
    294 
    295   __ lw(t2, MemOperand(a2, FrameDescription::pc_offset()));
    296   __ push(t2);
    297   __ lw(t2, MemOperand(a2, FrameDescription::continuation_offset()));
    298   __ push(t2);
    299 
    300 
    301   // Technically restoring 'at' should work unless zero_reg is also restored
    302   // but it's safer to check for this.
    303   ASSERT(!(at.bit() & restored_regs));
    304   // Restore the registers from the last output frame.
    305   __ mov(at, a2);
    306   for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
    307     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
    308     if ((restored_regs & (1 << i)) != 0) {
    309       __ lw(ToRegister(i), MemOperand(at, offset));
    310     }
    311   }
    312 
    313   __ InitializeRootRegister();
    314 
    315   __ pop(at);  // Get continuation, leave pc on stack.
    316   __ pop(ra);
    317   __ Jump(at);
    318   __ stop("Unreachable.");
    319 }
    320 
    321 
    322 // Maximum size of a table entry generated below.
    323 const int Deoptimizer::table_entry_size_ = 7 * Assembler::kInstrSize;
    324 
    325 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
    326   Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
    327 
    328   // Create a sequence of deoptimization entries.
    329   // Note that registers are still live when jumping to an entry.
    330   Label table_start;
    331   __ bind(&table_start);
    332   for (int i = 0; i < count(); i++) {
    333     Label start;
    334     __ bind(&start);
    335     __ addiu(sp, sp, -1 * kPointerSize);
    336     // Jump over the remaining deopt entries (including this one).
    337     // This code is always reached by calling Jump, which puts the target (label
    338     // start) into t9.
    339     const int remaining_entries = (count() - i) * table_entry_size_;
    340     __ Addu(t9, t9, remaining_entries);
    341     // 'at' was clobbered so we can only load the current entry value here.
    342     __ li(at, i);
    343     __ jr(t9);  // Expose delay slot.
    344     __ sw(at, MemOperand(sp, 0 * kPointerSize));  // In the delay slot.
    345 
    346     // Pad the rest of the code.
    347     while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) {
    348       __ nop();
    349     }
    350 
    351     ASSERT_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
    352   }
    353 
    354   ASSERT_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
    355       count() * table_entry_size_);
    356 }
    357 
    358 
    359 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
    360   SetFrameSlot(offset, value);
    361 }
    362 
    363 
    364 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
    365   SetFrameSlot(offset, value);
    366 }
    367 
    368 
    369 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
    370   // No out-of-line constant pool support.
    371   UNREACHABLE();
    372 }
    373 
    374 
    375 #undef __
    376 
    377 
    378 } }  // namespace v8::internal
    379