V8 Project
deoptimizer-x64.cc
Go to the documentation of this file.
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 
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  deopt_data->SetSharedFunctionInfo(Smi::FromInt(0));
64  // For each LLazyBailout instruction insert a call to the corresponding
65  // deoptimization entry.
66  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
67  if (deopt_data->Pc(i)->value() == -1) continue;
68  // Position where Call will be patched in.
69  Address call_address = instruction_start + deopt_data->Pc(i)->value();
70  // There is room enough to write a long call instruction because we pad
71  // LLazyBailout instructions with nops if necessary.
72  CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
73  patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
75  DCHECK(prev_call_address == NULL ||
76  call_address >= prev_call_address + patch_size());
77  DCHECK(call_address + patch_size() <= code->instruction_end());
78 #ifdef DEBUG
79  prev_call_address = call_address;
80 #endif
81  }
82 }
83 
84 
85 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
86  // Set the register values. The values are not important as there are no
87  // callee saved registers in JavaScript frames, so all registers are
88  // spilled. Registers rbp and rsp are set to the correct values though.
89  for (int i = 0; i < Register::kNumRegisters; i++) {
90  input_->SetRegister(i, i * 4);
91  }
92  input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
93  input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
94  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
95  input_->SetDoubleRegister(i, 0.0);
96  }
97 
98  // Fill the frame content from the actual data on the frame.
99  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
101  }
102 }
103 
104 
106  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
107  intptr_t handler =
108  reinterpret_cast<intptr_t>(descriptor->deoptimization_handler());
109  int params = descriptor->GetHandlerParameterCount();
110  output_frame->SetRegister(rax.code(), params);
111  output_frame->SetRegister(rbx.code(), handler);
112 }
113 
114 
115 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
116  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
117  double double_value = input_->GetDoubleRegister(i);
118  output_frame->SetDoubleRegister(i, double_value);
119  }
120 }
121 
122 
123 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
124  // There is no dynamic alignment padding on x64 in the input frame.
125  return false;
126 }
127 
128 
129 #define __ masm()->
130 
131 void Deoptimizer::EntryGenerator::Generate() {
132  GeneratePrologue();
133 
134  // Save all general purpose registers before messing with them.
136 
137  const int kDoubleRegsSize = kDoubleSize *
139  __ subp(rsp, Immediate(kDoubleRegsSize));
140 
141  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
142  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
143  int offset = i * kDoubleSize;
144  __ movsd(Operand(rsp, offset), xmm_reg);
145  }
146 
147  // We push all registers onto the stack, even though we do not need
148  // to restore all later.
149  for (int i = 0; i < kNumberOfRegisters; i++) {
150  Register r = Register::from_code(i);
151  __ pushq(r);
152  }
153 
154  const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize +
155  kDoubleRegsSize;
156 
157  // We use this to keep the value of the fifth argument temporarily.
158  // Unfortunately we can't store it directly in r8 (used for passing
159  // this on linux), since it is another parameter passing register on windows.
160  Register arg5 = r11;
161 
162  // Get the bailout id from the stack.
163  __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
164 
165  // Get the address of the location in the code object
166  // and compute the fp-to-sp delta in register arg5.
167  __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize));
168  __ leap(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize +
169  kPCOnStackSize));
170 
171  __ subp(arg5, rbp);
172  __ negp(arg5);
173 
174  // Allocate a new deoptimizer object.
175  __ PrepareCallCFunction(6);
177  __ movp(arg_reg_1, rax);
178  __ Set(arg_reg_2, type());
179  // Args 3 and 4 are already in the right registers.
180 
181  // On windows put the arguments on the stack (PrepareCallCFunction
182  // has created space for this). On linux pass the arguments in r8 and r9.
183 #ifdef _WIN64
184  __ movq(Operand(rsp, 4 * kRegisterSize), arg5);
185  __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
186  __ movq(Operand(rsp, 5 * kRegisterSize), arg5);
187 #else
188  __ movp(r8, arg5);
189  __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
190 #endif
191 
192  { AllowExternalCallThatCantCauseGC scope(masm());
193  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
194  }
195  // Preserve deoptimizer object in register rax and get the input
196  // frame descriptor pointer.
197  __ movp(rbx, Operand(rax, Deoptimizer::input_offset()));
198 
199  // Fill in the input registers.
200  for (int i = kNumberOfRegisters -1; i >= 0; i--) {
202  __ PopQuad(Operand(rbx, offset));
203  }
204 
205  // Fill in the double input registers.
206  int double_regs_offset = FrameDescription::double_registers_offset();
207  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
208  int dst_offset = i * kDoubleSize + double_regs_offset;
209  __ popq(Operand(rbx, dst_offset));
210  }
211 
212  // Remove the bailout id and return address from the stack.
213  __ addp(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize));
214 
215  // Compute a pointer to the unwinding limit in register rcx; that is
216  // the first stack slot not part of the input frame.
217  __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
218  __ addp(rcx, rsp);
219 
220  // Unwind the stack down to - but not including - the unwinding
221  // limit and copy the contents of the activation frame to the input
222  // frame description.
224  Label pop_loop_header;
225  __ jmp(&pop_loop_header);
226  Label pop_loop;
227  __ bind(&pop_loop);
228  __ Pop(Operand(rdx, 0));
229  __ addp(rdx, Immediate(sizeof(intptr_t)));
230  __ bind(&pop_loop_header);
231  __ cmpp(rcx, rsp);
232  __ j(not_equal, &pop_loop);
233 
234  // Compute the output frame in the deoptimizer.
235  __ pushq(rax);
236  __ PrepareCallCFunction(2);
237  __ movp(arg_reg_1, rax);
238  __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
239  {
240  AllowExternalCallThatCantCauseGC scope(masm());
241  __ CallCFunction(
242  ExternalReference::compute_output_frames_function(isolate()), 2);
243  }
244  __ popq(rax);
245 
246  // Replace the current frame with the output frames.
247  Label outer_push_loop, inner_push_loop,
248  outer_loop_header, inner_loop_header;
249  // Outer loop state: rax = current FrameDescription**, rdx = one past the
250  // last FrameDescription**.
251  __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
252  __ movp(rax, Operand(rax, Deoptimizer::output_offset()));
253  __ leap(rdx, Operand(rax, rdx, times_pointer_size, 0));
254  __ jmp(&outer_loop_header);
255  __ bind(&outer_push_loop);
256  // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
257  __ movp(rbx, Operand(rax, 0));
258  __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
259  __ jmp(&inner_loop_header);
260  __ bind(&inner_push_loop);
261  __ subp(rcx, Immediate(sizeof(intptr_t)));
263  __ bind(&inner_loop_header);
264  __ testp(rcx, rcx);
265  __ j(not_zero, &inner_push_loop);
266  __ addp(rax, Immediate(kPointerSize));
267  __ bind(&outer_loop_header);
268  __ cmpp(rax, rdx);
269  __ j(below, &outer_push_loop);
270 
271  for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
272  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
273  int src_offset = i * kDoubleSize + double_regs_offset;
274  __ movsd(xmm_reg, Operand(rbx, src_offset));
275  }
276 
277  // Push state, pc, and continuation from the last output frame.
279  __ PushQuad(Operand(rbx, FrameDescription::pc_offset()));
280  __ PushQuad(Operand(rbx, FrameDescription::continuation_offset()));
281 
282  // Push the registers from the last output frame.
283  for (int i = 0; i < kNumberOfRegisters; i++) {
285  __ PushQuad(Operand(rbx, offset));
286  }
287 
288  // Restore the registers from the stack.
289  for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
290  Register r = Register::from_code(i);
291  // Do not restore rsp, simply pop the value into the next register
292  // and overwrite this afterwards.
293  if (r.is(rsp)) {
294  DCHECK(i > 0);
295  r = Register::from_code(i - 1);
296  }
297  __ popq(r);
298  }
299 
300  // Set up the roots register.
301  __ InitializeRootRegister();
302  __ InitializeSmiConstantRegister();
303 
304  // Return to the continuation point.
305  __ ret(0);
306 }
307 
308 
310  // Create a sequence of deoptimization entries.
311  Label done;
312  for (int i = 0; i < count(); i++) {
313  int start = masm()->pc_offset();
314  USE(start);
315  __ pushq_imm32(i);
316  __ jmp(&done);
317  DCHECK(masm()->pc_offset() - start == table_entry_size_);
318  }
319  __ bind(&done);
320 }
321 
322 
323 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
324  if (kPCOnStackSize == 2 * kPointerSize) {
325  // Zero out the high-32 bit of PC for x32 port.
326  SetFrameSlot(offset + kPointerSize, 0);
327  }
328  SetFrameSlot(offset, value);
329 }
330 
331 
332 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
333  if (kFPOnStackSize == 2 * kPointerSize) {
334  // Zero out the high-32 bit of FP for x32 port.
335  SetFrameSlot(offset + kPointerSize, 0);
336  }
337  SetFrameSlot(offset, value);
338 }
339 
340 
341 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
342  // No out-of-line constant pool support.
343  UNREACHABLE();
344 }
345 
346 
347 #undef __
348 
349 
350 } } // namespace v8::internal
351 
352 #endif // V8_TARGET_ARCH_X64
static const int kCallSequenceLength
static RelocInfo::Mode RelocInfoNone()
static int output_offset()
Definition: deoptimizer.h:239
static const int table_entry_size_
Definition: deoptimizer.h:451
void CopyDoubleRegisters(FrameDescription *output_frame)
static Address GetDeoptimizationEntry(Isolate *isolate, int id, BailoutType type, GetEntryMode mode=ENSURE_ENTRY_CODE)
Definition: deoptimizer.cc:672
static void PatchCodeForDeoptimization(Isolate *isolate, Code *code)
static int output_count_offset()
Definition: deoptimizer.h:236
void SetPlatformCompiledStubRegisters(FrameDescription *output_frame, CodeStubDescriptor *desc)
bool HasAlignmentPadding(JSFunction *function)
FrameDescription * input_
Definition: deoptimizer.h:415
Isolate * isolate() const
Definition: deoptimizer.h:292
void FillInputFrame(Address tos, JavaScriptFrame *frame)
void SetCallerFp(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:477
void SetCallerConstantPool(unsigned offset, intptr_t value)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:518
static int double_registers_offset()
Definition: deoptimizer.h:574
void SetRegister(unsigned n, intptr_t value)
Definition: deoptimizer.h:523
void SetCallerPc(unsigned offset, intptr_t value)
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:495
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:528
static uintptr_t & uintptr_at(Address addr)
Definition: v8memory.h:48
static Smi * FromInt(int value)
Definition: objects-inl.h:1321
#define __
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be NULL
#define UNREACHABLE()
Definition: logging.h:30
#define DCHECK(condition)
Definition: logging.h:205
void USE(T)
Definition: macros.h:322
static int Push(SpecialRPOStackFrame *stack, int depth, BasicBlock *child, int unvisited)
Definition: scheduler.cc:773
const int kPointerSize
Definition: globals.h:129
const int kPCOnStackSize
Definition: globals.h:135
const int kDoubleSize
Definition: globals.h:127
const Register arg_reg_4
const Register rbp
const Register r11
const int kRegisterSize
Definition: globals.h:133
const Register r9
const Register rbx
byte * Address
Definition: globals.h:101
const Register r8
const int kFPOnStackSize
Definition: globals.h:136
const Register rdx
const Register arg_reg_1
const Register rax
const unsigned kNumberOfRegisters
static const int kNoCodeAgeSequenceLength
const Register rcx
const Register rsp
const Register arg_reg_3
const Register arg_reg_2
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
static const int kNumRegisters
Definition: assembler-arm.h:95
static Register from_code(int code)
static XMMRegister FromAllocationIndex(int index)
static int NumAllocatableRegisters()