V8 Project
deoptimizer-x87.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_X87
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 const int Deoptimizer::table_entry_size_ = 10;
18 
19 
22 }
23 
24 
26  Isolate* isolate = code->GetIsolate();
27  HandleScope scope(isolate);
28 
29  // Compute the size of relocation information needed for the code
30  // patching in Deoptimizer::DeoptimizeFunction.
31  int min_reloc_size = 0;
32  int prev_pc_offset = 0;
33  DeoptimizationInputData* deopt_data =
34  DeoptimizationInputData::cast(code->deoptimization_data());
35  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
36  int pc_offset = deopt_data->Pc(i)->value();
37  if (pc_offset == -1) continue;
38  DCHECK_GE(pc_offset, prev_pc_offset);
39  int pc_delta = pc_offset - prev_pc_offset;
40  // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
41  // if encodable with small pc delta encoding and up to 6 bytes
42  // otherwise.
43  if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
44  min_reloc_size += 2;
45  } else {
46  min_reloc_size += 6;
47  }
48  prev_pc_offset = pc_offset;
49  }
50 
51  // If the relocation information is not big enough we create a new
52  // relocation info object that is padded with comments to make it
53  // big enough for lazy doptimization.
54  int reloc_length = code->relocation_info()->length();
55  if (min_reloc_size > reloc_length) {
56  int comment_reloc_size = RelocInfo::kMinRelocCommentSize;
57  // Padding needed.
58  int min_padding = min_reloc_size - reloc_length;
59  // Number of comments needed to take up at least that much space.
60  int additional_comments =
61  (min_padding + comment_reloc_size - 1) / comment_reloc_size;
62  // Actual padding size.
63  int padding = additional_comments * comment_reloc_size;
64  // Allocate new relocation info and copy old relocation to the end
65  // of the new relocation info array because relocation info is
66  // written and read backwards.
67  Factory* factory = isolate->factory();
68  Handle<ByteArray> new_reloc =
69  factory->NewByteArray(reloc_length + padding, TENURED);
70  MemCopy(new_reloc->GetDataStartAddress() + padding,
71  code->relocation_info()->GetDataStartAddress(), reloc_length);
72  // Create a relocation writer to write the comments in the padding
73  // space. Use position 0 for everything to ensure short encoding.
74  RelocInfoWriter reloc_info_writer(
75  new_reloc->GetDataStartAddress() + padding, 0);
76  intptr_t comment_string
77  = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString);
78  RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL);
79  for (int i = 0; i < additional_comments; ++i) {
80 #ifdef DEBUG
81  byte* pos_before = reloc_info_writer.pos();
82 #endif
83  reloc_info_writer.Write(&rinfo);
85  pos_before - reloc_info_writer.pos());
86  }
87  // Replace relocation information on the code object.
88  code->set_relocation_info(*new_reloc);
89  }
90 }
91 
92 
93 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
94  Address code_start_address = code->instruction_start();
95 
96  if (FLAG_zap_code_space) {
97  // Fail hard and early if we enter this code object again.
98  byte* pointer = code->FindCodeAgeSequence();
99  if (pointer != NULL) {
100  pointer += kNoCodeAgeSequenceLength;
101  } else {
102  pointer = code->instruction_start();
103  }
104  CodePatcher patcher(pointer, 1);
105  patcher.masm()->int3();
106 
107  DeoptimizationInputData* data =
108  DeoptimizationInputData::cast(code->deoptimization_data());
109  int osr_offset = data->OsrPcOffset()->value();
110  if (osr_offset > 0) {
111  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
112  osr_patcher.masm()->int3();
113  }
114  }
115 
116  // We will overwrite the code's relocation info in-place. Relocation info
117  // is written backward. The relocation info is the payload of a byte
118  // array. Later on we will slide this to the start of the byte array and
119  // create a filler object in the remaining space.
120  ByteArray* reloc_info = code->relocation_info();
121  Address reloc_end_address = reloc_info->address() + reloc_info->Size();
122  RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
123 
124  // Since the call is a relative encoding, write new
125  // reloc info. We do not need any of the existing reloc info because the
126  // existing code will not be used again (we zap it in debug builds).
127  //
128  // Emit call to lazy deoptimization at all lazy deopt points.
129  DeoptimizationInputData* deopt_data =
130  DeoptimizationInputData::cast(code->deoptimization_data());
131 #ifdef DEBUG
132  Address prev_call_address = NULL;
133 #endif
134  // For each LLazyBailout instruction insert a call to the corresponding
135  // deoptimization entry.
136  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
137  if (deopt_data->Pc(i)->value() == -1) continue;
138  // Patch lazy deoptimization entry.
139  Address call_address = code_start_address + deopt_data->Pc(i)->value();
140  CodePatcher patcher(call_address, patch_size());
141  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
142  patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
143  // We use RUNTIME_ENTRY for deoptimization bailouts.
144  RelocInfo rinfo(call_address + 1, // 1 after the call opcode.
146  reinterpret_cast<intptr_t>(deopt_entry),
147  NULL);
148  reloc_info_writer.Write(&rinfo);
149  DCHECK_GE(reloc_info_writer.pos(),
150  reloc_info->address() + ByteArray::kHeaderSize);
151  DCHECK(prev_call_address == NULL ||
152  call_address >= prev_call_address + patch_size());
153  DCHECK(call_address + patch_size() <= code->instruction_end());
154 #ifdef DEBUG
155  prev_call_address = call_address;
156 #endif
157  }
158 
159  // Move the relocation info to the beginning of the byte array.
160  int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
161  MemMove(code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
162 
163  // The relocation info is in place, update the size.
164  reloc_info->set_length(new_reloc_size);
165 
166  // Handle the junk part after the new relocation info. We will create
167  // a non-live object in the extra space at the end of the former reloc info.
168  Address junk_address = reloc_info->address() + reloc_info->Size();
169  DCHECK(junk_address <= reloc_end_address);
170  isolate->heap()->CreateFillerObjectAt(junk_address,
171  reloc_end_address - junk_address);
172 }
173 
174 
175 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
176  // Set the register values. The values are not important as there are no
177  // callee saved registers in JavaScript frames, so all registers are
178  // spilled. Registers ebp and esp are set to the correct values though.
179 
180  for (int i = 0; i < Register::kNumRegisters; i++) {
181  input_->SetRegister(i, i * 4);
182  }
183  input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
184  input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
185  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
186  input_->SetDoubleRegister(i, 0.0);
187  }
188 
189  // Fill the frame content from the actual data on the frame.
190  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
192  }
193 }
194 
195 
197  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
198  intptr_t handler =
199  reinterpret_cast<intptr_t>(descriptor->deoptimization_handler());
200  int params = descriptor->GetHandlerParameterCount();
201  output_frame->SetRegister(eax.code(), params);
202  output_frame->SetRegister(ebx.code(), handler);
203 }
204 
205 
206 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
207  for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) {
208  double double_value = input_->GetDoubleRegister(i);
209  output_frame->SetDoubleRegister(i, double_value);
210  }
211 }
212 
213 
214 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
215  int parameter_count = function->shared()->formal_parameter_count() + 1;
216  unsigned input_frame_size = input_->GetFrameSize();
217  unsigned alignment_state_offset =
218  input_frame_size - parameter_count * kPointerSize -
220  kPointerSize;
223  int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
224  return (alignment_state == kAlignmentPaddingPushed);
225 }
226 
227 
228 #define __ masm()->
229 
230 void Deoptimizer::EntryGenerator::Generate() {
231  GeneratePrologue();
232 
233  // Save all general purpose registers before messing with them.
235 
236  const int kDoubleRegsSize =
238 
239  // Reserve space for x87 fp registers.
240  __ sub(esp, Immediate(kDoubleRegsSize));
241 
242  __ pushad();
243 
244  // GP registers are safe to use now.
245  // Save used x87 fp registers in correct position of previous reserve space.
246  Label loop, done;
247  // Get the layout of x87 stack.
248  __ sub(esp, Immediate(kPointerSize));
249  __ fistp_s(MemOperand(esp, 0));
250  __ pop(eax);
251  // Preserve stack layout in edi
252  __ mov(edi, eax);
253  // Get the x87 stack depth, the first 3 bits.
254  __ mov(ecx, eax);
255  __ and_(ecx, 0x7);
256  __ j(zero, &done, Label::kNear);
257 
258  __ bind(&loop);
259  __ shr(eax, 0x3);
260  __ mov(ebx, eax);
261  __ and_(ebx, 0x7); // Extract the st_x index into ebx.
262  // Pop TOS to the correct position. The disp(0x20) is due to pushad.
263  // The st_i should be saved to (esp + ebx * kDoubleSize + 0x20).
264  __ fstp_d(Operand(esp, ebx, times_8, 0x20));
265  __ dec(ecx); // Decrease stack depth.
266  __ j(not_zero, &loop, Label::kNear);
267  __ bind(&done);
268 
269  const int kSavedRegistersAreaSize =
270  kNumberOfRegisters * kPointerSize + kDoubleRegsSize;
271 
272  // Get the bailout id from the stack.
273  __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
274 
275  // Get the address of the location in the code object
276  // and compute the fp-to-sp delta in register edx.
277  __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
278  __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
279 
280  __ sub(edx, ebp);
281  __ neg(edx);
282 
283  __ push(edi);
284  // Allocate a new deoptimizer object.
285  __ PrepareCallCFunction(6, eax);
287  __ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
288  __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
289  __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
290  __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
291  __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
292  __ mov(Operand(esp, 5 * kPointerSize),
293  Immediate(ExternalReference::isolate_address(isolate())));
294  {
295  AllowExternalCallThatCantCauseGC scope(masm());
296  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
297  }
298 
299  __ pop(edi);
300 
301  // Preserve deoptimizer object in register eax and get the input
302  // frame descriptor pointer.
303  __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
304 
305  // Fill in the input registers.
306  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
308  __ pop(Operand(ebx, offset));
309  }
310 
311  int double_regs_offset = FrameDescription::double_registers_offset();
312  // Fill in the double input registers.
313  for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) {
314  int dst_offset = i * kDoubleSize + double_regs_offset;
315  int src_offset = i * kDoubleSize;
316  __ fld_d(Operand(esp, src_offset));
317  __ fstp_d(Operand(ebx, dst_offset));
318  }
319 
320  // Clear FPU all exceptions.
321  // TODO(ulan): Find out why the TOP register is not zero here in some cases,
322  // and check that the generated code never deoptimizes with unbalanced stack.
323  __ fnclex();
324 
325  // Remove the bailout id, return address and the double registers.
326  __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
327 
328  // Compute a pointer to the unwinding limit in register ecx; that is
329  // the first stack slot not part of the input frame.
330  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
331  __ add(ecx, esp);
332 
333  // Unwind the stack down to - but not including - the unwinding
334  // limit and copy the contents of the activation frame to the input
335  // frame description.
337  Label pop_loop_header;
338  __ jmp(&pop_loop_header);
339  Label pop_loop;
340  __ bind(&pop_loop);
341  __ pop(Operand(edx, 0));
342  __ add(edx, Immediate(sizeof(uint32_t)));
343  __ bind(&pop_loop_header);
344  __ cmp(ecx, esp);
345  __ j(not_equal, &pop_loop);
346 
347  // Compute the output frame in the deoptimizer.
348  __ push(edi);
349  __ push(eax);
350  __ PrepareCallCFunction(1, ebx);
351  __ mov(Operand(esp, 0 * kPointerSize), eax);
352  {
353  AllowExternalCallThatCantCauseGC scope(masm());
354  __ CallCFunction(
355  ExternalReference::compute_output_frames_function(isolate()), 1);
356  }
357  __ pop(eax);
358  __ pop(edi);
359 
360  // If frame was dynamically aligned, pop padding.
361  Label no_padding;
363  Immediate(0));
364  __ j(equal, &no_padding);
365  __ pop(ecx);
366  if (FLAG_debug_code) {
367  __ cmp(ecx, Immediate(kAlignmentZapValue));
368  __ Assert(equal, kAlignmentMarkerExpected);
369  }
370  __ bind(&no_padding);
371 
372  // Replace the current frame with the output frames.
373  Label outer_push_loop, inner_push_loop,
374  outer_loop_header, inner_loop_header;
375  // Outer loop state: eax = current FrameDescription**, edx = one past the
376  // last FrameDescription**.
377  __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
378  __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
379  __ lea(edx, Operand(eax, edx, times_4, 0));
380  __ jmp(&outer_loop_header);
381  __ bind(&outer_push_loop);
382  // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
383  __ mov(ebx, Operand(eax, 0));
384  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
385  __ jmp(&inner_loop_header);
386  __ bind(&inner_push_loop);
387  __ sub(ecx, Immediate(sizeof(uint32_t)));
389  __ bind(&inner_loop_header);
390  __ test(ecx, ecx);
391  __ j(not_zero, &inner_push_loop);
392  __ add(eax, Immediate(kPointerSize));
393  __ bind(&outer_loop_header);
394  __ cmp(eax, edx);
395  __ j(below, &outer_push_loop);
396 
397 
398  // In case of a failed STUB, we have to restore the x87 stack.
399  // x87 stack layout is in edi.
400  Label loop2, done2;
401  // Get the x87 stack depth, the first 3 bits.
402  __ mov(ecx, edi);
403  __ and_(ecx, 0x7);
404  __ j(zero, &done2, Label::kNear);
405 
406  __ lea(ecx, Operand(ecx, ecx, times_2, 0));
407  __ bind(&loop2);
408  __ mov(eax, edi);
409  __ shr_cl(eax);
410  __ and_(eax, 0x7);
411  __ fld_d(Operand(ebx, eax, times_8, double_regs_offset));
412  __ sub(ecx, Immediate(0x3));
413  __ j(not_zero, &loop2, Label::kNear);
414  __ bind(&done2);
415 
416  // Push state, pc, and continuation from the last output frame.
417  __ push(Operand(ebx, FrameDescription::state_offset()));
418  __ push(Operand(ebx, FrameDescription::pc_offset()));
419  __ push(Operand(ebx, FrameDescription::continuation_offset()));
420 
421 
422  // Push the registers from the last output frame.
423  for (int i = 0; i < kNumberOfRegisters; i++) {
425  __ push(Operand(ebx, offset));
426  }
427 
428  // Restore the registers from the stack.
429  __ popad();
430 
431  // Return to the continuation point.
432  __ ret(0);
433 }
434 
435 
437  // Create a sequence of deoptimization entries.
438  Label done;
439  for (int i = 0; i < count(); i++) {
440  int start = masm()->pc_offset();
441  USE(start);
442  __ push_imm32(i);
443  __ jmp(&done);
444  DCHECK(masm()->pc_offset() - start == table_entry_size_);
445  }
446  __ bind(&done);
447 }
448 
449 
450 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
451  SetFrameSlot(offset, value);
452 }
453 
454 
455 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
456  SetFrameSlot(offset, value);
457 }
458 
459 
460 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
461  // No out-of-line constant pool support.
462  UNREACHABLE();
463 }
464 
465 
466 #undef __
467 
468 
469 } } // namespace v8::internal
470 
471 #endif // V8_TARGET_ARCH_X87
static const int kCallInstructionLength
static int has_alignment_padding_offset()
Definition: deoptimizer.h:241
static int output_offset()
Definition: deoptimizer.h:239
static void EnsureRelocSpaceForLazyDeoptimization(Handle< Code > code)
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)
static const int kHeaderSize
Definition: objects.h:2393
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
intptr_t GetFrameSlot(unsigned offset)
Definition: deoptimizer.h:486
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
void CreateFillerObjectAt(Address addr, int size)
Definition: heap.cc:3221
Factory * factory()
Definition: isolate.h:982
static const int kDynamicAlignmentStateOffset
Definition: frames-ia32.h:78
static uint32_t & uint32_at(Address addr)
Definition: v8memory.h:24
static const char *const kFillerCommentString
Definition: assembler.h:323
static const int kMaxSmallPCDelta
Definition: assembler.h:334
static const int kMinRelocCommentSize
Definition: assembler.h:328
static const int kFixedFrameSize
Definition: frames.h:158
#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_GE(v1, v2)
Definition: logging.h:208
#define DCHECK(condition)
Definition: logging.h:205
void USE(T)
Definition: macros.h:322
int int32_t
Definition: unicode.cc:24
const int kPointerSize
Definition: globals.h:129
const Register edx
const Register edi
const int kAlignmentPaddingPushed
Definition: frames-ia32.h:32
const Register esp
const int kDoubleSize
Definition: globals.h:127
void MemMove(void *dest, const void *src, size_t size)
Definition: utils.h:353
const Register eax
const Register ebx
byte * Address
Definition: globals.h:101
OStream & dec(OStream &os)
Definition: ostreams.cc:122
const int kAlignmentZapValue
Definition: frames-ia32.h:33
const Register ebp
const unsigned kNumberOfRegisters
static const int kNoCodeAgeSequenceLength
void MemCopy(void *dest, const void *src, size_t size)
Definition: utils.h:350
const Register ecx
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
static const int kNumRegisters
Definition: assembler-arm.h:95
static const int kMaxNumAllocatableRegisters