deoptimizer-ia32.cc 15.8 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/v8.h"
6

7
#if V8_TARGET_ARCH_IA32
8

9 10 11 12
#include "src/codegen.h"
#include "src/deoptimizer.h"
#include "src/full-codegen.h"
#include "src/safepoint-table.h"
13 14 15 16

namespace v8 {
namespace internal {

17
const int Deoptimizer::table_entry_size_ = 10;
18

19 20 21 22 23 24

int Deoptimizer::patch_size() {
  return Assembler::kCallInstructionLength;
}


25
void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
26 27
  Isolate* isolate = code->GetIsolate();
  HandleScope scope(isolate);
28 29

  // Compute the size of relocation information needed for the code
30
  // patching in Deoptimizer::PatchCodeForDeoptimization below.
31
  int min_reloc_size = 0;
32 33 34 35 36 37
  int prev_pc_offset = 0;
  DeoptimizationInputData* deopt_data =
      DeoptimizationInputData::cast(code->deoptimization_data());
  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
    int pc_offset = deopt_data->Pc(i)->value();
    if (pc_offset == -1) continue;
38
    DCHECK_GE(pc_offset, prev_pc_offset);
39 40 41 42 43 44 45 46
    int pc_delta = pc_offset - prev_pc_offset;
    // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
    // if encodable with small pc delta encoding and up to 6 bytes
    // otherwise.
    if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
      min_reloc_size += 2;
    } else {
      min_reloc_size += 6;
47
    }
48
    prev_pc_offset = pc_offset;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
  }

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


93
void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
94
  Address code_start_address = code->instruction_start();
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

  if (FLAG_zap_code_space) {
    // Fail hard and early if we enter this code object again.
    byte* pointer = code->FindCodeAgeSequence();
    if (pointer != NULL) {
      pointer += kNoCodeAgeSequenceLength;
    } else {
      pointer = code->instruction_start();
    }
    CodePatcher patcher(pointer, 1);
    patcher.masm()->int3();

    DeoptimizationInputData* data =
        DeoptimizationInputData::cast(code->deoptimization_data());
    int osr_offset = data->OsrPcOffset()->value();
    if (osr_offset > 0) {
      CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
      osr_patcher.masm()->int3();
    }
  }

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

124
  // Since the call is a relative encoding, write new
125 126
  // reloc info.  We do not need any of the existing reloc info because the
  // existing code will not be used again (we zap it in debug builds).
127 128 129 130 131 132 133
  //
  // Emit call to lazy deoptimization at all lazy deopt points.
  DeoptimizationInputData* deopt_data =
      DeoptimizationInputData::cast(code->deoptimization_data());
#ifdef DEBUG
  Address prev_call_address = NULL;
#endif
134 135
  // For each LLazyBailout instruction insert a call to the corresponding
  // deoptimization entry.
136 137 138 139 140
  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
    if (deopt_data->Pc(i)->value() == -1) continue;
    // Patch lazy deoptimization entry.
    Address call_address = code_start_address + deopt_data->Pc(i)->value();
    CodePatcher patcher(call_address, patch_size());
141
    Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
142
    patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
143 144 145 146 147 148
    // We use RUNTIME_ENTRY for deoptimization bailouts.
    RelocInfo rinfo(call_address + 1,  // 1 after the call opcode.
                    RelocInfo::RUNTIME_ENTRY,
                    reinterpret_cast<intptr_t>(deopt_entry),
                    NULL);
    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 155 156
#ifdef DEBUG
    prev_call_address = call_address;
#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 167

  // Handle the junk part after the new relocation info. We will create
  // 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 171
  isolate->heap()->CreateFillerObjectAt(junk_address,
                                        reloc_end_address - junk_address);
172 173 174
}


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

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

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


196
void Deoptimizer::SetPlatformCompiledStubRegisters(
197
    FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
198
  intptr_t handler =
199
      reinterpret_cast<intptr_t>(descriptor->deoptimization_handler());
200
  int params = descriptor->GetHandlerParameterCount();
201 202 203 204 205 206
  output_frame->SetRegister(eax.code(), params);
  output_frame->SetRegister(ebx.code(), handler);
}


void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
207
  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
208 209 210 211 212 213
    double double_value = input_->GetDoubleRegister(i);
    output_frame->SetDoubleRegister(i, double_value);
  }
}


214
bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
215 216
  int parameter_count =
      function->shared()->internal_formal_parameter_count() + 1;
217 218 219 220 221
  unsigned input_frame_size = input_->GetFrameSize();
  unsigned alignment_state_offset =
      input_frame_size - parameter_count * kPointerSize -
      StandardFrameConstants::kFixedFrameSize -
      kPointerSize;
222
  DCHECK(JavaScriptFrameConstants::kDynamicAlignmentStateOffset ==
223 224 225 226 227 228
      JavaScriptFrameConstants::kLocal0Offset);
  int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
  return (alignment_state == kAlignmentPaddingPushed);
}


229 230
#define __ masm()->

231
void Deoptimizer::TableEntryGenerator::Generate() {
232 233 234 235 236 237
  GeneratePrologue();

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

  const int kDoubleRegsSize = kDoubleSize *
238
                              XMMRegister::kMaxNumAllocatableRegisters;
239
  __ sub(esp, Immediate(kDoubleRegsSize));
240
  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
241 242 243
    XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    int offset = i * kDoubleSize;
    __ movsd(Operand(esp, offset), xmm_reg);
244 245 246 247
  }

  __ pushad();

248 249 250
  ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate());
  __ mov(Operand::StaticVariable(c_entry_fp_address), ebp);

251 252 253 254 255 256
  const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
                                      kDoubleRegsSize;

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

257
  // Get the address of the location in the code object
258
  // and compute the fp-to-sp delta in register edx.
259 260 261
  __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
  __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));

262
  __ sub(edx, ebp);
263 264 265
  __ neg(edx);

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

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

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

  int double_regs_offset = FrameDescription::double_registers_offset();
291
  // Fill in the double input registers.
292
  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
293 294 295 296
    int dst_offset = i * kDoubleSize + double_regs_offset;
    int src_offset = i * kDoubleSize;
    __ movsd(xmm0, Operand(esp, src_offset));
    __ movsd(Operand(ebx, dst_offset), xmm0);
297
  }
298

299 300 301
  // Clear FPU all exceptions.
  // TODO(ulan): Find out why the TOP register is not zero here in some cases,
  // and check that the generated code never deoptimizes with unbalanced stack.
302
  __ fnclex();
303

304 305
  // Remove the bailout id, return address and the double registers.
  __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
306 307 308 309

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

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

  // Compute the output frame in the deoptimizer.
  __ push(eax);
  __ PrepareCallCFunction(1, ebx);
  __ mov(Operand(esp, 0 * kPointerSize), eax);
330 331 332
  {
    AllowExternalCallThatCantCauseGC scope(masm());
    __ CallCFunction(
333
        ExternalReference::compute_output_frames_function(isolate()), 1);
334
  }
335 336
  __ pop(eax);

337 338 339 340 341 342 343 344 345
  // If frame was dynamically aligned, pop padding.
  Label no_padding;
  __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
         Immediate(0));
  __ j(equal, &no_padding);
  __ pop(ecx);
  if (FLAG_debug_code) {
    __ cmp(ecx, Immediate(kAlignmentZapValue));
    __ Assert(equal, kAlignmentMarkerExpected);
346
  }
347
  __ bind(&no_padding);
348

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

374
  // In case of a failed STUB, we have to restore the XMM registers.
375
  for (int i = 0; i < XMMRegister::kMaxNumAllocatableRegisters; ++i) {
376 377 378
    XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
    int src_offset = i * kDoubleSize + double_regs_offset;
    __ movsd(xmm_reg, Operand(ebx, src_offset));
379 380 381
  }

  // Push state, pc, and continuation from the last output frame.
382
  __ push(Operand(ebx, FrameDescription::state_offset()));
383 384 385 386 387 388
  __ push(Operand(ebx, FrameDescription::pc_offset()));
  __ push(Operand(ebx, FrameDescription::continuation_offset()));


  // Push the registers from the last output frame.
  for (int i = 0; i < kNumberOfRegisters; i++) {
389
    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
    __ push(Operand(ebx, offset));
  }

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

  // Return to the continuation point.
  __ ret(0);
}


void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
  // Create a sequence of deoptimization entries.
  Label done;
  for (int i = 0; i < count(); i++) {
    int start = masm()->pc_offset();
    USE(start);
    __ push_imm32(i);
    __ jmp(&done);
409
    DCHECK(masm()->pc_offset() - start == table_entry_size_);
410 411 412 413
  }
  __ bind(&done);
}

414 415 416 417 418 419 420 421 422 423 424

void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
  SetFrameSlot(offset, value);
}


void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
  SetFrameSlot(offset, value);
}


425 426 427 428 429 430
void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
  // No out-of-line constant pool support.
  UNREACHABLE();
}


431 432 433 434
#undef __


} }  // namespace v8::internal
435 436

#endif  // V8_TARGET_ARCH_IA32