V8 Project
deoptimizer-ia32.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_IA32
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 < XMMRegister::kMaxNumAllocatableRegisters; 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 < XMMRegister::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 = kDoubleSize *
238  __ sub(esp, Immediate(kDoubleRegsSize));
239  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
240  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
241  int offset = i * kDoubleSize;
242  __ movsd(Operand(esp, offset), xmm_reg);
243  }
244 
245  __ pushad();
246 
247  const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
248  kDoubleRegsSize;
249 
250  // Get the bailout id from the stack.
251  __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
252 
253  // Get the address of the location in the code object
254  // and compute the fp-to-sp delta in register edx.
255  __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
256  __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
257 
258  __ sub(edx, ebp);
259  __ neg(edx);
260 
261  // Allocate a new deoptimizer object.
262  __ PrepareCallCFunction(6, eax);
264  __ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
265  __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
266  __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
267  __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
268  __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
269  __ mov(Operand(esp, 5 * kPointerSize),
270  Immediate(ExternalReference::isolate_address(isolate())));
271  {
272  AllowExternalCallThatCantCauseGC scope(masm());
273  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
274  }
275 
276  // Preserve deoptimizer object in register eax and get the input
277  // frame descriptor pointer.
278  __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
279 
280  // Fill in the input registers.
281  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
283  __ pop(Operand(ebx, offset));
284  }
285 
286  int double_regs_offset = FrameDescription::double_registers_offset();
287  // Fill in the double input registers.
288  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
289  int dst_offset = i * kDoubleSize + double_regs_offset;
290  int src_offset = i * kDoubleSize;
291  __ movsd(xmm0, Operand(esp, src_offset));
292  __ movsd(Operand(ebx, dst_offset), xmm0);
293  }
294 
295  // Clear FPU all exceptions.
296  // TODO(ulan): Find out why the TOP register is not zero here in some cases,
297  // and check that the generated code never deoptimizes with unbalanced stack.
298  __ fnclex();
299 
300  // Remove the bailout id, return address and the double registers.
301  __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
302 
303  // Compute a pointer to the unwinding limit in register ecx; that is
304  // the first stack slot not part of the input frame.
305  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
306  __ add(ecx, esp);
307 
308  // Unwind the stack down to - but not including - the unwinding
309  // limit and copy the contents of the activation frame to the input
310  // frame description.
312  Label pop_loop_header;
313  __ jmp(&pop_loop_header);
314  Label pop_loop;
315  __ bind(&pop_loop);
316  __ pop(Operand(edx, 0));
317  __ add(edx, Immediate(sizeof(uint32_t)));
318  __ bind(&pop_loop_header);
319  __ cmp(ecx, esp);
320  __ j(not_equal, &pop_loop);
321 
322  // Compute the output frame in the deoptimizer.
323  __ push(eax);
324  __ PrepareCallCFunction(1, ebx);
325  __ mov(Operand(esp, 0 * kPointerSize), eax);
326  {
327  AllowExternalCallThatCantCauseGC scope(masm());
328  __ CallCFunction(
329  ExternalReference::compute_output_frames_function(isolate()), 1);
330  }
331  __ pop(eax);
332 
333  // If frame was dynamically aligned, pop padding.
334  Label no_padding;
336  Immediate(0));
337  __ j(equal, &no_padding);
338  __ pop(ecx);
339  if (FLAG_debug_code) {
340  __ cmp(ecx, Immediate(kAlignmentZapValue));
341  __ Assert(equal, kAlignmentMarkerExpected);
342  }
343  __ bind(&no_padding);
344 
345  // Replace the current frame with the output frames.
346  Label outer_push_loop, inner_push_loop,
347  outer_loop_header, inner_loop_header;
348  // Outer loop state: eax = current FrameDescription**, edx = one past the
349  // last FrameDescription**.
350  __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
351  __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
352  __ lea(edx, Operand(eax, edx, times_4, 0));
353  __ jmp(&outer_loop_header);
354  __ bind(&outer_push_loop);
355  // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
356  __ mov(ebx, Operand(eax, 0));
357  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
358  __ jmp(&inner_loop_header);
359  __ bind(&inner_push_loop);
360  __ sub(ecx, Immediate(sizeof(uint32_t)));
362  __ bind(&inner_loop_header);
363  __ test(ecx, ecx);
364  __ j(not_zero, &inner_push_loop);
365  __ add(eax, Immediate(kPointerSize));
366  __ bind(&outer_loop_header);
367  __ cmp(eax, edx);
368  __ j(below, &outer_push_loop);
369 
370  // In case of a failed STUB, we have to restore the XMM registers.
371  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
372  XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
373  int src_offset = i * kDoubleSize + double_regs_offset;
374  __ movsd(xmm_reg, Operand(ebx, src_offset));
375  }
376 
377  // Push state, pc, and continuation from the last output frame.
378  __ push(Operand(ebx, FrameDescription::state_offset()));
379  __ push(Operand(ebx, FrameDescription::pc_offset()));
380  __ push(Operand(ebx, FrameDescription::continuation_offset()));
381 
382 
383  // Push the registers from the last output frame.
384  for (int i = 0; i < kNumberOfRegisters; i++) {
386  __ push(Operand(ebx, offset));
387  }
388 
389  // Restore the registers from the stack.
390  __ popad();
391 
392  // Return to the continuation point.
393  __ ret(0);
394 }
395 
396 
398  // Create a sequence of deoptimization entries.
399  Label done;
400  for (int i = 0; i < count(); i++) {
401  int start = masm()->pc_offset();
402  USE(start);
403  __ push_imm32(i);
404  __ jmp(&done);
405  DCHECK(masm()->pc_offset() - start == table_entry_size_);
406  }
407  __ bind(&done);
408 }
409 
410 
411 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
412  SetFrameSlot(offset, value);
413 }
414 
415 
416 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
417  SetFrameSlot(offset, value);
418 }
419 
420 
421 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
422  // No out-of-line constant pool support.
423  UNREACHABLE();
424 }
425 
426 
427 #undef __
428 
429 
430 } } // namespace v8::internal
431 
432 #endif // V8_TARGET_ARCH_IA32
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 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
const XMMRegister xmm0
byte * Address
Definition: globals.h:101
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 XMMRegister FromAllocationIndex(int index)
static const int kMaxNumAllocatableRegisters