v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
deoptimizer-x64.cc
Go to the documentation of this file.
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 
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  if (FLAG_zap_code_space) {
55  // Fail hard and early if we enter this code object again.
56  byte* pointer = code->FindCodeAgeSequence();
57  if (pointer != NULL) {
58  pointer += kNoCodeAgeSequenceLength;
59  } else {
60  pointer = code->instruction_start();
61  }
62  CodePatcher patcher(pointer, 1);
63  patcher.masm()->int3();
64 
65  DeoptimizationInputData* data =
66  DeoptimizationInputData::cast(code->deoptimization_data());
67  int osr_offset = data->OsrPcOffset()->value();
68  if (osr_offset > 0) {
69  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
70  osr_patcher.masm()->int3();
71  }
72  }
73 
74  // For each LLazyBailout instruction insert a absolute call to the
75  // corresponding deoptimization entry, or a short call to an absolute
76  // jump if space is short. The absolute jumps are put in a table just
77  // before the safepoint table (space was allocated there when the Code
78  // object was created, if necessary).
79 
80  Address instruction_start = code->instruction_start();
81 #ifdef DEBUG
82  Address prev_call_address = NULL;
83 #endif
84  DeoptimizationInputData* deopt_data =
85  DeoptimizationInputData::cast(code->deoptimization_data());
86  SharedFunctionInfo* shared =
87  SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
88  shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
89  deopt_data->SetSharedFunctionInfo(Smi::FromInt(0));
90  // For each LLazyBailout instruction insert a call to the corresponding
91  // deoptimization entry.
92  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
93  if (deopt_data->Pc(i)->value() == -1) continue;
94  // Position where Call will be patched in.
95  Address call_address = instruction_start + deopt_data->Pc(i)->value();
96  // There is room enough to write a long call instruction because we pad
97  // LLazyBailout instructions with nops if necessary.
98  CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
99  patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
101  ASSERT(prev_call_address == NULL ||
102  call_address >= prev_call_address + patch_size());
103  ASSERT(call_address + patch_size() <= code->instruction_end());
104 #ifdef DEBUG
105  prev_call_address = call_address;
106 #endif
107  }
108 }
109 
110 
111 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
112  // Set the register values. The values are not important as there are no
113  // callee saved registers in JavaScript frames, so all registers are
114  // spilled. Registers rbp and rsp are set to the correct values though.
115  for (int i = 0; i < Register::kNumRegisters; i++) {
116  input_->SetRegister(i, i * 4);
117  }
118  input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
119  input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
120  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
121  input_->SetDoubleRegister(i, 0.0);
122  }
123 
124  // Fill the frame content from the actual data on the frame.
125  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
126  input_->SetFrameSlot(i, Memory::uintptr_at(tos + i));
127  }
128 }
129 
130 
131 void Deoptimizer::SetPlatformCompiledStubRegisters(
132  FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
133  intptr_t handler =
134  reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
135  int params = descriptor->GetHandlerParameterCount();
136  output_frame->SetRegister(rax.code(), params);
137  output_frame->SetRegister(rbx.code(), handler);
138 }
139 
140 
141 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
142  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
143  double double_value = input_->GetDoubleRegister(i);
144  output_frame->SetDoubleRegister(i, double_value);
145  }
146 }
147 
148 
149 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
150  // There is no dynamic alignment padding on x64 in the input frame.
151  return false;
152 }
153 
154 
155 Code* Deoptimizer::NotifyStubFailureBuiltin() {
156  return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
157 }
158 
159 
160 #define __ masm()->
161 
162 void Deoptimizer::EntryGenerator::Generate() {
163  GeneratePrologue();
164 
165  // Save all general purpose registers before messing with them.
167 
168  const int kDoubleRegsSize = kDoubleSize *
170  __ subp(rsp, Immediate(kDoubleRegsSize));
171 
172  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
173  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
174  int offset = i * kDoubleSize;
175  __ movsd(Operand(rsp, offset), xmm_reg);
176  }
177 
178  // We push all registers onto the stack, even though we do not need
179  // to restore all later.
180  for (int i = 0; i < kNumberOfRegisters; i++) {
181  Register r = Register::from_code(i);
182  __ pushq(r);
183  }
184 
185  const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize +
186  kDoubleRegsSize;
187 
188  // We use this to keep the value of the fifth argument temporarily.
189  // Unfortunately we can't store it directly in r8 (used for passing
190  // this on linux), since it is another parameter passing register on windows.
191  Register arg5 = r11;
192 
193  // Get the bailout id from the stack.
194  __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
195 
196  // Get the address of the location in the code object
197  // and compute the fp-to-sp delta in register arg5.
198  __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize));
199  __ leap(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize +
200  kPCOnStackSize));
201 
202  __ subp(arg5, rbp);
203  __ negp(arg5);
204 
205  // Allocate a new deoptimizer object.
206  __ PrepareCallCFunction(6);
208  __ movp(arg_reg_1, rax);
209  __ Set(arg_reg_2, type());
210  // Args 3 and 4 are already in the right registers.
211 
212  // On windows put the arguments on the stack (PrepareCallCFunction
213  // has created space for this). On linux pass the arguments in r8 and r9.
214 #ifdef _WIN64
215  __ movq(Operand(rsp, 4 * kRegisterSize), arg5);
216  __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
217  __ movq(Operand(rsp, 5 * kRegisterSize), arg5);
218 #else
219  __ movp(r8, arg5);
220  __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
221 #endif
222 
223  { AllowExternalCallThatCantCauseGC scope(masm());
224  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
225  }
226  // Preserve deoptimizer object in register rax and get the input
227  // frame descriptor pointer.
228  __ movp(rbx, Operand(rax, Deoptimizer::input_offset()));
229 
230  // Fill in the input registers.
231  for (int i = kNumberOfRegisters -1; i >= 0; i--) {
232  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
233  __ Pop(Operand(rbx, offset));
234  }
235 
236  // Fill in the double input registers.
237  int double_regs_offset = FrameDescription::double_registers_offset();
238  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
239  int dst_offset = i * kDoubleSize + double_regs_offset;
240  __ popq(Operand(rbx, dst_offset));
241  }
242 
243  // Remove the bailout id and return address from the stack.
244  __ addp(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize));
245 
246  // Compute a pointer to the unwinding limit in register rcx; that is
247  // the first stack slot not part of the input frame.
248  __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
249  __ addp(rcx, rsp);
250 
251  // Unwind the stack down to - but not including - the unwinding
252  // limit and copy the contents of the activation frame to the input
253  // frame description.
255  Label pop_loop_header;
256  __ jmp(&pop_loop_header);
257  Label pop_loop;
258  __ bind(&pop_loop);
259  __ Pop(Operand(rdx, 0));
260  __ addp(rdx, Immediate(sizeof(intptr_t)));
261  __ bind(&pop_loop_header);
262  __ cmpp(rcx, rsp);
263  __ j(not_equal, &pop_loop);
264 
265  // Compute the output frame in the deoptimizer.
266  __ pushq(rax);
267  __ PrepareCallCFunction(2);
268  __ movp(arg_reg_1, rax);
269  __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
270  {
271  AllowExternalCallThatCantCauseGC scope(masm());
272  __ CallCFunction(
273  ExternalReference::compute_output_frames_function(isolate()), 2);
274  }
275  __ popq(rax);
276 
277  // Replace the current frame with the output frames.
278  Label outer_push_loop, inner_push_loop,
279  outer_loop_header, inner_loop_header;
280  // Outer loop state: rax = current FrameDescription**, rdx = one past the
281  // last FrameDescription**.
282  __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
283  __ movp(rax, Operand(rax, Deoptimizer::output_offset()));
284  __ leap(rdx, Operand(rax, rdx, times_pointer_size, 0));
285  __ jmp(&outer_loop_header);
286  __ bind(&outer_push_loop);
287  // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
288  __ movp(rbx, Operand(rax, 0));
289  __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
290  __ jmp(&inner_loop_header);
291  __ bind(&inner_push_loop);
292  __ subp(rcx, Immediate(sizeof(intptr_t)));
294  __ bind(&inner_loop_header);
295  __ testp(rcx, rcx);
296  __ j(not_zero, &inner_push_loop);
297  __ addp(rax, Immediate(kPointerSize));
298  __ bind(&outer_loop_header);
299  __ cmpp(rax, rdx);
300  __ j(below, &outer_push_loop);
301 
302  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
303  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
304  int src_offset = i * kDoubleSize + double_regs_offset;
305  __ movsd(xmm_reg, Operand(rbx, src_offset));
306  }
307 
308  // Push state, pc, and continuation from the last output frame.
309  __ Push(Operand(rbx, FrameDescription::state_offset()));
310  __ Push(Operand(rbx, FrameDescription::pc_offset()));
311  __ Push(Operand(rbx, FrameDescription::continuation_offset()));
312 
313  // Push the registers from the last output frame.
314  for (int i = 0; i < kNumberOfRegisters; i++) {
315  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
316  __ Push(Operand(rbx, offset));
317  }
318 
319  // Restore the registers from the stack.
320  for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
321  Register r = Register::from_code(i);
322  // Do not restore rsp, simply pop the value into the next register
323  // and overwrite this afterwards.
324  if (r.is(rsp)) {
325  ASSERT(i > 0);
326  r = Register::from_code(i - 1);
327  }
328  __ popq(r);
329  }
330 
331  // Set up the roots register.
332  __ InitializeRootRegister();
333  __ InitializeSmiConstantRegister();
334 
335  // Return to the continuation point.
336  __ ret(0);
337 }
338 
339 
341  // Create a sequence of deoptimization entries.
342  Label done;
343  for (int i = 0; i < count(); i++) {
344  int start = masm()->pc_offset();
345  USE(start);
346  __ pushq_imm32(i);
347  __ jmp(&done);
348  ASSERT(masm()->pc_offset() - start == table_entry_size_);
349  }
350  __ bind(&done);
351 }
352 
353 
354 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
355  SetFrameSlot(offset, value);
356 }
357 
358 
359 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
360  SetFrameSlot(offset, value);
361 }
362 
363 
364 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
365  // No out-of-line constant pool support.
366  UNREACHABLE();
367 }
368 
369 
370 #undef __
371 
372 
373 } } // namespace v8::internal
374 
375 #endif // V8_TARGET_ARCH_X64
byte * Address
Definition: globals.h:186
const Register rdx
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
Definition: flags.cc:269
static RelocInfo::Mode RelocInfoNone()
void SetCallerPc(unsigned offset, intptr_t value)
Code * builtin(Name name)
Definition: builtins.h:322
const Register r11
static Smi * FromInt(int value)
Definition: objects-inl.h:1209
const Register rbp
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:503
Builtins * builtins()
Definition: isolate.h:948
static XMMRegister FromAllocationIndex(int index)
#define ASSERT(condition)
Definition: checks.h:329
static SharedFunctionInfo * cast(Object *obj)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:526
static const int kNumRegisters
static int double_registers_offset()
Definition: deoptimizer.h:582
uint8_t byte
Definition: globals.h:185
#define UNREACHABLE()
Definition: checks.h:52
static int output_offset()
Definition: deoptimizer.h:250
const int kDoubleSize
Definition: globals.h:266
static Address GetDeoptimizationEntry(Isolate *isolate, int id, BailoutType type, GetEntryMode mode=ENSURE_ENTRY_CODE)
Definition: deoptimizer.cc:683
const Register r9
const int kPointerSize
Definition: globals.h:268
const int kRegisterSize
Definition: globals.h:269
const Register rbx
const Register rsp
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra code(assertions) for debugging") DEFINE_bool(code_comments
#define __
void SetRegister(unsigned n, intptr_t value)
Definition: deoptimizer.h:531
static Register from_code(int code)
const Register rax
void SetCallerConstantPool(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:485
static int output_count_offset()
Definition: deoptimizer.h:247
static const int kCallSequenceLength
const unsigned kNumberOfRegisters
const Register arg_reg_1
const Register r8
const Register arg_reg_4
const Register rcx
static int NumAllocatableRegisters()
void USE(T)
Definition: globals.h:341
const Register arg_reg_2
virtual void GeneratePrologue()
static uintptr_t & uintptr_at(Address addr)
Definition: v8memory.h:71
const int kPCOnStackSize
Definition: globals.h:270
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:536
void SetCallerFp(unsigned offset, intptr_t value)
void EvictFromOptimizedCodeMap(Code *optimized_code, const char *reason)
Definition: objects.cc:9593
const Register arg_reg_3
static DeoptimizationInputData * cast(Object *obj)