The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / ia32 / deoptimizer-ia32.cc @ f230a1cf

History | View | Annotate | Download (16.6 KB)

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_IA32
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
const int Deoptimizer::table_entry_size_ = 10;
41

    
42

    
43
int Deoptimizer::patch_size() {
44
  return Assembler::kCallInstructionLength;
45
}
46

    
47

    
48
void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
49
  Isolate* isolate = code->GetIsolate();
50
  HandleScope scope(isolate);
51

    
52
  // Compute the size of relocation information needed for the code
53
  // patching in Deoptimizer::DeoptimizeFunction.
54
  int min_reloc_size = 0;
55
  int prev_pc_offset = 0;
56
  DeoptimizationInputData* deopt_data =
57
      DeoptimizationInputData::cast(code->deoptimization_data());
58
  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
59
    int pc_offset = deopt_data->Pc(i)->value();
60
    if (pc_offset == -1) continue;
61
    ASSERT_GE(pc_offset, prev_pc_offset);
62
    int pc_delta = pc_offset - prev_pc_offset;
63
    // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
64
    // if encodable with small pc delta encoding and up to 6 bytes
65
    // otherwise.
66
    if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
67
      min_reloc_size += 2;
68
    } else {
69
      min_reloc_size += 6;
70
    }
71
    prev_pc_offset = pc_offset;
72
  }
73

    
74
  // If the relocation information is not big enough we create a new
75
  // relocation info object that is padded with comments to make it
76
  // big enough for lazy doptimization.
77
  int reloc_length = code->relocation_info()->length();
78
  if (min_reloc_size > reloc_length) {
79
    int comment_reloc_size = RelocInfo::kMinRelocCommentSize;
80
    // Padding needed.
81
    int min_padding = min_reloc_size - reloc_length;
82
    // Number of comments needed to take up at least that much space.
83
    int additional_comments =
84
        (min_padding + comment_reloc_size - 1) / comment_reloc_size;
85
    // Actual padding size.
86
    int padding = additional_comments * comment_reloc_size;
87
    // Allocate new relocation info and copy old relocation to the end
88
    // of the new relocation info array because relocation info is
89
    // written and read backwards.
90
    Factory* factory = isolate->factory();
91
    Handle<ByteArray> new_reloc =
92
        factory->NewByteArray(reloc_length + padding, TENURED);
93
    OS::MemCopy(new_reloc->GetDataStartAddress() + padding,
94
                code->relocation_info()->GetDataStartAddress(),
95
                reloc_length);
96
    // Create a relocation writer to write the comments in the padding
97
    // space. Use position 0 for everything to ensure short encoding.
98
    RelocInfoWriter reloc_info_writer(
99
        new_reloc->GetDataStartAddress() + padding, 0);
100
    intptr_t comment_string
101
        = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString);
102
    RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL);
103
    for (int i = 0; i < additional_comments; ++i) {
104
#ifdef DEBUG
105
      byte* pos_before = reloc_info_writer.pos();
106
#endif
107
      reloc_info_writer.Write(&rinfo);
108
      ASSERT(RelocInfo::kMinRelocCommentSize ==
109
             pos_before - reloc_info_writer.pos());
110
    }
111
    // Replace relocation information on the code object.
112
    code->set_relocation_info(*new_reloc);
113
  }
114
}
115

    
116

    
117
void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
118
  Address code_start_address = code->instruction_start();
119
  // We will overwrite the code's relocation info in-place. Relocation info
120
  // is written backward. The relocation info is the payload of a byte
121
  // array.  Later on we will slide this to the start of the byte array and
122
  // create a filler object in the remaining space.
123
  ByteArray* reloc_info = code->relocation_info();
124
  Address reloc_end_address = reloc_info->address() + reloc_info->Size();
125
  RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
126

    
127
  // For each LLazyBailout instruction insert a call to the corresponding
128
  // deoptimization entry.
129

    
130
  // Since the call is a relative encoding, write new
131
  // reloc info.  We do not need any of the existing reloc info because the
132
  // existing code will not be used again (we zap it in debug builds).
133
  //
134
  // Emit call to lazy deoptimization at all lazy deopt points.
135
  DeoptimizationInputData* deopt_data =
136
      DeoptimizationInputData::cast(code->deoptimization_data());
137
#ifdef DEBUG
138
  Address prev_call_address = NULL;
139
#endif
140
  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
141
    if (deopt_data->Pc(i)->value() == -1) continue;
142
    // Patch lazy deoptimization entry.
143
    Address call_address = code_start_address + deopt_data->Pc(i)->value();
144
    CodePatcher patcher(call_address, patch_size());
145
    Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
146
    patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
147
    // We use RUNTIME_ENTRY for deoptimization bailouts.
148
    RelocInfo rinfo(call_address + 1,  // 1 after the call opcode.
149
                    RelocInfo::RUNTIME_ENTRY,
150
                    reinterpret_cast<intptr_t>(deopt_entry),
151
                    NULL);
152
    reloc_info_writer.Write(&rinfo);
153
    ASSERT_GE(reloc_info_writer.pos(),
154
              reloc_info->address() + ByteArray::kHeaderSize);
155
    ASSERT(prev_call_address == NULL ||
156
           call_address >= prev_call_address + patch_size());
157
    ASSERT(call_address + patch_size() <= code->instruction_end());
158
#ifdef DEBUG
159
    prev_call_address = call_address;
160
#endif
161
  }
162

    
163
  // Move the relocation info to the beginning of the byte array.
164
  int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
165
  OS::MemMove(
166
      code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
167

    
168
  // The relocation info is in place, update the size.
169
  reloc_info->set_length(new_reloc_size);
170

    
171
  // Handle the junk part after the new relocation info. We will create
172
  // a non-live object in the extra space at the end of the former reloc info.
173
  Address junk_address = reloc_info->address() + reloc_info->Size();
174
  ASSERT(junk_address <= reloc_end_address);
175
  isolate->heap()->CreateFillerObjectAt(junk_address,
176
                                        reloc_end_address - junk_address);
177
}
178

    
179

    
180
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
181
  // Set the register values. The values are not important as there are no
182
  // callee saved registers in JavaScript frames, so all registers are
183
  // spilled. Registers ebp and esp are set to the correct values though.
184

    
185
  for (int i = 0; i < Register::kNumRegisters; i++) {
186
    input_->SetRegister(i, i * 4);
187
  }
188
  input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
189
  input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
190
  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
191
    input_->SetDoubleRegister(i, 0.0);
192
  }
193

    
194
  // Fill the frame content from the actual data on the frame.
195
  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
196
    input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
197
  }
198
}
199

    
200

    
201
void Deoptimizer::SetPlatformCompiledStubRegisters(
202
    FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
203
  intptr_t handler =
204
      reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
205
  int params = descriptor->environment_length();
206
  output_frame->SetRegister(eax.code(), params);
207
  output_frame->SetRegister(ebx.code(), handler);
208
}
209

    
210

    
211
void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
212
  if (!CpuFeatures::IsSupported(SSE2)) return;
213
  for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
214
    double double_value = input_->GetDoubleRegister(i);
215
    output_frame->SetDoubleRegister(i, double_value);
216
  }
217
}
218

    
219

    
220
bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
221
  int parameter_count = function->shared()->formal_parameter_count() + 1;
222
  unsigned input_frame_size = input_->GetFrameSize();
223
  unsigned alignment_state_offset =
224
      input_frame_size - parameter_count * kPointerSize -
225
      StandardFrameConstants::kFixedFrameSize -
226
      kPointerSize;
227
  ASSERT(JavaScriptFrameConstants::kDynamicAlignmentStateOffset ==
228
      JavaScriptFrameConstants::kLocal0Offset);
229
  int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
230
  return (alignment_state == kAlignmentPaddingPushed);
231
}
232

    
233

    
234
#define __ masm()->
235

    
236
void Deoptimizer::EntryGenerator::Generate() {
237
  GeneratePrologue();
238

    
239
  // Save all general purpose registers before messing with them.
240
  const int kNumberOfRegisters = Register::kNumRegisters;
241

    
242
  const int kDoubleRegsSize = kDoubleSize *
243
                              XMMRegister::kNumAllocatableRegisters;
244
  __ sub(esp, Immediate(kDoubleRegsSize));
245
  if (CpuFeatures::IsSupported(SSE2)) {
246
    CpuFeatureScope scope(masm(), SSE2);
247
    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
248
      XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
249
      int offset = i * kDoubleSize;
250
      __ movsd(Operand(esp, offset), xmm_reg);
251
    }
252
  }
253

    
254
  __ pushad();
255

    
256
  const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
257
                                      kDoubleRegsSize;
258

    
259
  // Get the bailout id from the stack.
260
  __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
261

    
262
  // Get the address of the location in the code object
263
  // and compute the fp-to-sp delta in register edx.
264
  __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
265
  __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
266

    
267
  __ sub(edx, ebp);
268
  __ neg(edx);
269

    
270
  // Allocate a new deoptimizer object.
271
  __ PrepareCallCFunction(6, eax);
272
  __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
273
  __ mov(Operand(esp, 0 * kPointerSize), eax);  // Function.
274
  __ mov(Operand(esp, 1 * kPointerSize), Immediate(type()));  // Bailout type.
275
  __ mov(Operand(esp, 2 * kPointerSize), ebx);  // Bailout id.
276
  __ mov(Operand(esp, 3 * kPointerSize), ecx);  // Code address or 0.
277
  __ mov(Operand(esp, 4 * kPointerSize), edx);  // Fp-to-sp delta.
278
  __ mov(Operand(esp, 5 * kPointerSize),
279
         Immediate(ExternalReference::isolate_address(isolate())));
280
  {
281
    AllowExternalCallThatCantCauseGC scope(masm());
282
    __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
283
  }
284

    
285
  // Preserve deoptimizer object in register eax and get the input
286
  // frame descriptor pointer.
287
  __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
288

    
289
  // Fill in the input registers.
290
  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
291
    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
292
    __ pop(Operand(ebx, offset));
293
  }
294

    
295
  int double_regs_offset = FrameDescription::double_registers_offset();
296
  if (CpuFeatures::IsSupported(SSE2)) {
297
    CpuFeatureScope scope(masm(), SSE2);
298
    // Fill in the double input registers.
299
    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
300
      int dst_offset = i * kDoubleSize + double_regs_offset;
301
      int src_offset = i * kDoubleSize;
302
      __ movsd(xmm0, Operand(esp, src_offset));
303
      __ movsd(Operand(ebx, dst_offset), xmm0);
304
    }
305
  }
306

    
307
  // Clear FPU all exceptions.
308
  // TODO(ulan): Find out why the TOP register is not zero here in some cases,
309
  // and check that the generated code never deoptimizes with unbalanced stack.
310
  __ fnclex();
311

    
312
  // Remove the bailout id, return address and the double registers.
313
  __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
314

    
315
  // Compute a pointer to the unwinding limit in register ecx; that is
316
  // the first stack slot not part of the input frame.
317
  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
318
  __ add(ecx, esp);
319

    
320
  // Unwind the stack down to - but not including - the unwinding
321
  // limit and copy the contents of the activation frame to the input
322
  // frame description.
323
  __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset()));
324
  Label pop_loop_header;
325
  __ jmp(&pop_loop_header);
326
  Label pop_loop;
327
  __ bind(&pop_loop);
328
  __ pop(Operand(edx, 0));
329
  __ add(edx, Immediate(sizeof(uint32_t)));
330
  __ bind(&pop_loop_header);
331
  __ cmp(ecx, esp);
332
  __ j(not_equal, &pop_loop);
333

    
334
  // Compute the output frame in the deoptimizer.
335
  __ push(eax);
336
  __ PrepareCallCFunction(1, ebx);
337
  __ mov(Operand(esp, 0 * kPointerSize), eax);
338
  {
339
    AllowExternalCallThatCantCauseGC scope(masm());
340
    __ CallCFunction(
341
        ExternalReference::compute_output_frames_function(isolate()), 1);
342
  }
343
  __ pop(eax);
344

    
345
  // If frame was dynamically aligned, pop padding.
346
  Label no_padding;
347
  __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
348
         Immediate(0));
349
  __ j(equal, &no_padding);
350
  __ pop(ecx);
351
  if (FLAG_debug_code) {
352
    __ cmp(ecx, Immediate(kAlignmentZapValue));
353
    __ Assert(equal, kAlignmentMarkerExpected);
354
  }
355
  __ bind(&no_padding);
356

    
357
  // Replace the current frame with the output frames.
358
  Label outer_push_loop, inner_push_loop,
359
      outer_loop_header, inner_loop_header;
360
  // Outer loop state: eax = current FrameDescription**, edx = one past the
361
  // last FrameDescription**.
362
  __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
363
  __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
364
  __ lea(edx, Operand(eax, edx, times_4, 0));
365
  __ jmp(&outer_loop_header);
366
  __ bind(&outer_push_loop);
367
  // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
368
  __ mov(ebx, Operand(eax, 0));
369
  __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
370
  __ jmp(&inner_loop_header);
371
  __ bind(&inner_push_loop);
372
  __ sub(ecx, Immediate(sizeof(uint32_t)));
373
  __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset()));
374
  __ bind(&inner_loop_header);
375
  __ test(ecx, ecx);
376
  __ j(not_zero, &inner_push_loop);
377
  __ add(eax, Immediate(kPointerSize));
378
  __ bind(&outer_loop_header);
379
  __ cmp(eax, edx);
380
  __ j(below, &outer_push_loop);
381

    
382
  // In case of a failed STUB, we have to restore the XMM registers.
383
  if (CpuFeatures::IsSupported(SSE2)) {
384
    CpuFeatureScope scope(masm(), SSE2);
385
    for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
386
      XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
387
      int src_offset = i * kDoubleSize + double_regs_offset;
388
      __ movsd(xmm_reg, Operand(ebx, src_offset));
389
    }
390
  }
391

    
392
  // Push state, pc, and continuation from the last output frame.
393
  __ push(Operand(ebx, FrameDescription::state_offset()));
394
  __ push(Operand(ebx, FrameDescription::pc_offset()));
395
  __ push(Operand(ebx, FrameDescription::continuation_offset()));
396

    
397

    
398
  // Push the registers from the last output frame.
399
  for (int i = 0; i < kNumberOfRegisters; i++) {
400
    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
401
    __ push(Operand(ebx, offset));
402
  }
403

    
404
  // Restore the registers from the stack.
405
  __ popad();
406

    
407
  // Return to the continuation point.
408
  __ ret(0);
409
}
410

    
411

    
412
void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
413
  // Create a sequence of deoptimization entries.
414
  Label done;
415
  for (int i = 0; i < count(); i++) {
416
    int start = masm()->pc_offset();
417
    USE(start);
418
    __ push_imm32(i);
419
    __ jmp(&done);
420
    ASSERT(masm()->pc_offset() - start == table_entry_size_);
421
  }
422
  __ bind(&done);
423
}
424

    
425

    
426
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
427
  SetFrameSlot(offset, value);
428
}
429

    
430

    
431
void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
432
  SetFrameSlot(offset, value);
433
}
434

    
435

    
436
#undef __
437

    
438

    
439
} }  // namespace v8::internal
440

    
441
#endif  // V8_TARGET_ARCH_IA32