| OLD | NEW | 
| (Empty) |  | 
 |    1 // Copyright 2013 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 #include "codegen.h" | 
 |   31 #include "deoptimizer.h" | 
 |   32 #include "full-codegen.h" | 
 |   33 #include "safepoint-table.h" | 
 |   34  | 
 |   35  | 
 |   36 namespace v8 { | 
 |   37 namespace internal { | 
 |   38  | 
 |   39  | 
 |   40 int Deoptimizer::patch_size() { | 
 |   41   // Size of the code used to patch lazy bailout points. | 
 |   42   // Patching is done by Deoptimizer::DeoptimizeFunction. | 
 |   43   return 4 * kInstructionSize; | 
 |   44 } | 
 |   45  | 
 |   46  | 
 |   47  | 
 |   48 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { | 
 |   49   // Invalidate the relocation information, as it will become invalid by the | 
 |   50   // code patching below, and is not needed any more. | 
 |   51   code->InvalidateRelocation(); | 
 |   52  | 
 |   53   // For each LLazyBailout instruction insert a call to the corresponding | 
 |   54   // deoptimization entry. | 
 |   55   DeoptimizationInputData* deopt_data = | 
 |   56       DeoptimizationInputData::cast(code->deoptimization_data()); | 
 |   57   Address code_start_address = code->instruction_start(); | 
 |   58 #ifdef DEBUG | 
 |   59   Address prev_call_address = NULL; | 
 |   60 #endif | 
 |   61  | 
 |   62   for (int i = 0; i < deopt_data->DeoptCount(); i++) { | 
 |   63     if (deopt_data->Pc(i)->value() == -1) continue; | 
 |   64  | 
 |   65     Address call_address = code_start_address + deopt_data->Pc(i)->value(); | 
 |   66     Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY); | 
 |   67  | 
 |   68     PatchingAssembler patcher(call_address, patch_size() / kInstructionSize); | 
 |   69     patcher.LoadLiteral(ip0, 2 * kInstructionSize); | 
 |   70     patcher.blr(ip0); | 
 |   71     patcher.dc64(reinterpret_cast<intptr_t>(deopt_entry)); | 
 |   72  | 
 |   73     ASSERT((prev_call_address == NULL) || | 
 |   74            (call_address >= prev_call_address + patch_size())); | 
 |   75     ASSERT(call_address + patch_size() <= code->instruction_end()); | 
 |   76 #ifdef DEBUG | 
 |   77     prev_call_address = call_address; | 
 |   78 #endif | 
 |   79   } | 
 |   80 } | 
 |   81  | 
 |   82  | 
 |   83 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { | 
 |   84   // Set the register values. The values are not important as there are no | 
 |   85   // callee saved registers in JavaScript frames, so all registers are | 
 |   86   // spilled. Registers fp and sp are set to the correct values though. | 
 |   87   for (int i = 0; i < Register::NumRegisters(); i++) { | 
 |   88     input_->SetRegister(i, 0); | 
 |   89   } | 
 |   90  | 
 |   91   // TODO(all): Do we also need to set a value to csp? | 
 |   92   input_->SetRegister(jssp.code(), reinterpret_cast<intptr_t>(frame->sp())); | 
 |   93   input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp())); | 
 |   94  | 
 |   95   for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) { | 
 |   96     input_->SetDoubleRegister(i, 0.0); | 
 |   97   } | 
 |   98  | 
 |   99   // Fill the frame content from the actual data on the frame. | 
 |  100   for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { | 
 |  101     input_->SetFrameSlot(i, Memory::uint64_at(tos + i)); | 
 |  102   } | 
 |  103 } | 
 |  104  | 
 |  105  | 
 |  106 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) { | 
 |  107   // There is no dynamic alignment padding on A64 in the input frame. | 
 |  108   return false; | 
 |  109 } | 
 |  110  | 
 |  111  | 
 |  112 void Deoptimizer::SetPlatformCompiledStubRegisters( | 
 |  113     FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) { | 
 |  114   ApiFunction function(descriptor->deoptimization_handler_); | 
 |  115   ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_); | 
 |  116   intptr_t handler = reinterpret_cast<intptr_t>(xref.address()); | 
 |  117   int params = descriptor->GetHandlerParameterCount(); | 
 |  118   output_frame->SetRegister(x0.code(), params); | 
 |  119   output_frame->SetRegister(x1.code(), handler); | 
 |  120 } | 
 |  121  | 
 |  122  | 
 |  123 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { | 
 |  124   for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) { | 
 |  125     double double_value = input_->GetDoubleRegister(i); | 
 |  126     output_frame->SetDoubleRegister(i, double_value); | 
 |  127   } | 
 |  128 } | 
 |  129  | 
 |  130  | 
 |  131 Code* Deoptimizer::NotifyStubFailureBuiltin() { | 
 |  132   return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles); | 
 |  133 } | 
 |  134  | 
 |  135  | 
 |  136 #define __ masm()-> | 
 |  137  | 
 |  138 void Deoptimizer::EntryGenerator::Generate() { | 
 |  139   GeneratePrologue(); | 
 |  140  | 
 |  141   // TODO(all): This code needs to be revisited. We probably only need to save | 
 |  142   // caller-saved registers here. Callee-saved registers can be stored directly | 
 |  143   // in the input frame. | 
 |  144  | 
 |  145   // Save all allocatable floating point registers. | 
 |  146   CPURegList saved_fp_registers(CPURegister::kFPRegister, kDRegSize, | 
 |  147                                 0, FPRegister::NumAllocatableRegisters() - 1); | 
 |  148   __ PushCPURegList(saved_fp_registers); | 
 |  149  | 
 |  150   // We save all the registers expcept jssp, sp and lr. | 
 |  151   CPURegList saved_registers(CPURegister::kRegister, kXRegSize, 0, 27); | 
 |  152   saved_registers.Combine(fp); | 
 |  153   __ PushCPURegList(saved_registers); | 
 |  154  | 
 |  155   const int kSavedRegistersAreaSize = | 
 |  156       (saved_registers.Count() * kXRegSizeInBytes) + | 
 |  157       (saved_fp_registers.Count() * kDRegSizeInBytes); | 
 |  158  | 
 |  159   // Floating point registers are saved on the stack above core registers. | 
 |  160   const int kFPRegistersOffset = saved_registers.Count() * kXRegSizeInBytes; | 
 |  161  | 
 |  162   // Get the bailout id from the stack. | 
 |  163   Register bailout_id = x2; | 
 |  164   __ Peek(bailout_id, kSavedRegistersAreaSize); | 
 |  165  | 
 |  166   Register code_object = x3; | 
 |  167   Register fp_to_sp = x4; | 
 |  168   // Get the address of the location in the code object. This is the return | 
 |  169   // address for lazy deoptimization. | 
 |  170   __ Mov(code_object, lr); | 
 |  171   // Compute the fp-to-sp delta, and correct one word for bailout id. | 
 |  172   __ Add(fp_to_sp, masm()->StackPointer(), | 
 |  173          kSavedRegistersAreaSize + (1 * kPointerSize)); | 
 |  174   __ Sub(fp_to_sp, fp, fp_to_sp); | 
 |  175  | 
 |  176   // Allocate a new deoptimizer object. | 
 |  177   __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); | 
 |  178   __ Mov(x1, type()); | 
 |  179   // Following arguments are already loaded: | 
 |  180   //  - x2: bailout id | 
 |  181   //  - x3: code object address | 
 |  182   //  - x4: fp-to-sp delta | 
 |  183   __ Mov(x5, Operand(ExternalReference::isolate_address(isolate()))); | 
 |  184  | 
 |  185   { | 
 |  186     // Call Deoptimizer::New(). | 
 |  187     AllowExternalCallThatCantCauseGC scope(masm()); | 
 |  188     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); | 
 |  189   } | 
 |  190  | 
 |  191   // Preserve "deoptimizer" object in register x0. | 
 |  192   Register deoptimizer = x0; | 
 |  193  | 
 |  194   // Get the input frame descriptor pointer. | 
 |  195   __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset())); | 
 |  196  | 
 |  197   // Copy core registers into the input frame. | 
 |  198   CPURegList copy_to_input = saved_registers; | 
 |  199   for (int i = 0; i < saved_registers.Count(); i++) { | 
 |  200     // TODO(all): Look for opportunities to optimize this by using ldp/stp. | 
 |  201     __ Peek(x2, i * kPointerSize); | 
 |  202     CPURegister current_reg = copy_to_input.PopLowestIndex(); | 
 |  203     int offset = (current_reg.code() * kPointerSize) + | 
 |  204         FrameDescription::registers_offset(); | 
 |  205     __ Str(x2, MemOperand(x1, offset)); | 
 |  206   } | 
 |  207  | 
 |  208   // Copy FP registers to the input frame. | 
 |  209   for (int i = 0; i < saved_fp_registers.Count(); i++) { | 
 |  210     // TODO(all): Look for opportunities to optimize this by using ldp/stp. | 
 |  211     int dst_offset = FrameDescription::double_registers_offset() + | 
 |  212         (i * kDoubleSize); | 
 |  213     int src_offset = kFPRegistersOffset + (i * kDoubleSize); | 
 |  214     __ Peek(x2, src_offset); | 
 |  215     __ Str(x2, MemOperand(x1, dst_offset)); | 
 |  216   } | 
 |  217  | 
 |  218   // Remove the bailout id and the saved registers from the stack. | 
 |  219   __ Drop(1 + (kSavedRegistersAreaSize / kXRegSizeInBytes)); | 
 |  220  | 
 |  221   // Compute a pointer to the unwinding limit in register x2; that is | 
 |  222   // the first stack slot not part of the input frame. | 
 |  223   Register unwind_limit = x2; | 
 |  224   __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset())); | 
 |  225   __ Add(unwind_limit, unwind_limit, __ StackPointer()); | 
 |  226  | 
 |  227   // Unwind the stack down to - but not including - the unwinding | 
 |  228   // limit and copy the contents of the activation frame to the input | 
 |  229   // frame description. | 
 |  230   __ Add(x3, x1, FrameDescription::frame_content_offset()); | 
 |  231   Label pop_loop; | 
 |  232   Label pop_loop_header; | 
 |  233   __ B(&pop_loop_header); | 
 |  234   __ Bind(&pop_loop); | 
 |  235   __ Pop(x4); | 
 |  236   __ Str(x4, MemOperand(x3, kPointerSize, PostIndex)); | 
 |  237   __ Bind(&pop_loop_header); | 
 |  238   __ Cmp(unwind_limit, __ StackPointer()); | 
 |  239   __ B(ne, &pop_loop); | 
 |  240  | 
 |  241   // Compute the output frame in the deoptimizer. | 
 |  242   __ Push(x0);  // Preserve deoptimizer object across call. | 
 |  243  | 
 |  244   { | 
 |  245     // Call Deoptimizer::ComputeOutputFrames(). | 
 |  246     AllowExternalCallThatCantCauseGC scope(masm()); | 
 |  247     __ CallCFunction( | 
 |  248         ExternalReference::compute_output_frames_function(isolate()), 1); | 
 |  249   } | 
 |  250   __ Pop(x4);  // Restore deoptimizer object (class Deoptimizer). | 
 |  251  | 
 |  252   // Replace the current (input) frame with the output frames. | 
 |  253   Label outer_push_loop, inner_push_loop, | 
 |  254       outer_loop_header, inner_loop_header; | 
 |  255   __ Ldrsw(x1, MemOperand(x4, Deoptimizer::output_count_offset())); | 
 |  256   __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset())); | 
 |  257   __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2)); | 
 |  258   __ B(&outer_loop_header); | 
 |  259  | 
 |  260   __ Bind(&outer_push_loop); | 
 |  261   Register current_frame = x2; | 
 |  262   __ Ldr(current_frame, MemOperand(x0, 0)); | 
 |  263   __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset())); | 
 |  264   __ B(&inner_loop_header); | 
 |  265  | 
 |  266   __ Bind(&inner_push_loop); | 
 |  267   __ Sub(x3, x3, kPointerSize); | 
 |  268   __ Add(x6, current_frame, x3); | 
 |  269   __ Ldr(x7, MemOperand(x6, FrameDescription::frame_content_offset())); | 
 |  270   __ Push(x7); | 
 |  271   __ Bind(&inner_loop_header); | 
 |  272   __ Cbnz(x3, &inner_push_loop); | 
 |  273  | 
 |  274   __ Add(x0, x0, kPointerSize); | 
 |  275   __ Bind(&outer_loop_header); | 
 |  276   __ Cmp(x0, x1); | 
 |  277   __ B(lt, &outer_push_loop); | 
 |  278  | 
 |  279   __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset())); | 
 |  280   ASSERT(!saved_fp_registers.IncludesAliasOf(crankshaft_fp_scratch) && | 
 |  281          !saved_fp_registers.IncludesAliasOf(fp_zero) && | 
 |  282          !saved_fp_registers.IncludesAliasOf(fp_scratch)); | 
 |  283   int src_offset = FrameDescription::double_registers_offset(); | 
 |  284   while (!saved_fp_registers.IsEmpty()) { | 
 |  285     const CPURegister reg = saved_fp_registers.PopLowestIndex(); | 
 |  286     __ Ldr(reg, MemOperand(x1, src_offset)); | 
 |  287     src_offset += kDoubleSize; | 
 |  288   } | 
 |  289  | 
 |  290   // Push state from the last output frame. | 
 |  291   __ Ldr(x6, MemOperand(current_frame, FrameDescription::state_offset())); | 
 |  292   __ Push(x6); | 
 |  293  | 
 |  294   // TODO(all): ARM copies a lot (if not all) of the last output frame onto the | 
 |  295   // stack, then pops it all into registers. Here, we try to load it directly | 
 |  296   // into the relevant registers. Is this correct? If so, we should improve the | 
 |  297   // ARM code. | 
 |  298  | 
 |  299   // TODO(all): This code needs to be revisited, We probably don't need to | 
 |  300   // restore all the registers as fullcodegen does not keep live values in | 
 |  301   // registers (note that at least fp must be restored though). | 
 |  302  | 
 |  303   // Restore registers from the last output frame. | 
 |  304   // Note that lr is not in the list of saved_registers and will be restored | 
 |  305   // later. We can use it to hold the address of last output frame while | 
 |  306   // reloading the other registers. | 
 |  307   ASSERT(!saved_registers.IncludesAliasOf(lr)); | 
 |  308   Register last_output_frame = lr; | 
 |  309   __ Mov(last_output_frame, current_frame); | 
 |  310  | 
 |  311   // We don't need to restore x7 as it will be clobbered later to hold the | 
 |  312   // continuation address. | 
 |  313   Register continuation = x7; | 
 |  314   saved_registers.Remove(continuation); | 
 |  315  | 
 |  316   while (!saved_registers.IsEmpty()) { | 
 |  317     // TODO(all): Look for opportunities to optimize this by using ldp. | 
 |  318     CPURegister current_reg = saved_registers.PopLowestIndex(); | 
 |  319     int offset = (current_reg.code() * kPointerSize) + | 
 |  320         FrameDescription::registers_offset(); | 
 |  321     __ Ldr(current_reg, MemOperand(last_output_frame, offset)); | 
 |  322   } | 
 |  323  | 
 |  324   __ Ldr(continuation, MemOperand(last_output_frame, | 
 |  325                                   FrameDescription::continuation_offset())); | 
 |  326   __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset())); | 
 |  327   __ InitializeRootRegister(); | 
 |  328   __ Br(continuation); | 
 |  329 } | 
 |  330  | 
 |  331  | 
 |  332 // Size of an entry of the second level deopt table. | 
 |  333 // This is the code size generated by GeneratePrologue for one entry. | 
 |  334 const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize; | 
 |  335  | 
 |  336  | 
 |  337 void Deoptimizer::TableEntryGenerator::GeneratePrologue() { | 
 |  338   // Create a sequence of deoptimization entries. | 
 |  339   // Note that registers are still live when jumping to an entry. | 
 |  340   Label done; | 
 |  341   { | 
 |  342     InstructionAccurateScope scope(masm()); | 
 |  343  | 
 |  344     // The number of entry will never exceed kMaxNumberOfEntries. | 
 |  345     // As long as kMaxNumberOfEntries is a valid 16 bits immediate you can use | 
 |  346     // a movz instruction to load the entry id. | 
 |  347     ASSERT(is_uint16(Deoptimizer::kMaxNumberOfEntries)); | 
 |  348  | 
 |  349     for (int i = 0; i < count(); i++) { | 
 |  350       int start = masm()->pc_offset(); | 
 |  351       USE(start); | 
 |  352       __ movz(masm()->Tmp0(), i); | 
 |  353       __ b(&done); | 
 |  354       ASSERT(masm()->pc_offset() - start == table_entry_size_); | 
 |  355     } | 
 |  356   } | 
 |  357   __ Bind(&done); | 
 |  358   // TODO(all): We need to add some kind of assertion to verify that Tmp0() | 
 |  359   // is not clobbered by Push. | 
 |  360   __ Push(masm()->Tmp0()); | 
 |  361 } | 
 |  362  | 
 |  363  | 
 |  364 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { | 
 |  365   SetFrameSlot(offset, value); | 
 |  366 } | 
 |  367  | 
 |  368  | 
 |  369 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { | 
 |  370   SetFrameSlot(offset, value); | 
 |  371 } | 
 |  372  | 
 |  373  | 
 |  374 #undef __ | 
 |  375  | 
 |  376 } }  // namespace v8::internal | 
| OLD | NEW |