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 / mips / deoptimizer-mips.cc @ f230a1cf

History | View | Annotate | Download (13.9 KB)

1

    
2
// Copyright 2011 the V8 project authors. All rights reserved.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are
5
// met:
6
//
7
//     * Redistributions of source code must retain the above copyright
8
//       notice, this list of conditions and the following disclaimer.
9
//     * Redistributions in binary form must reproduce the above
10
//       copyright notice, this list of conditions and the following
11
//       disclaimer in the documentation and/or other materials provided
12
//       with the distribution.
13
//     * Neither the name of Google Inc. nor the names of its
14
//       contributors may be used to endorse or promote products derived
15
//       from this software without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28

    
29
#include "v8.h"
30

    
31
#include "codegen.h"
32
#include "deoptimizer.h"
33
#include "full-codegen.h"
34
#include "safepoint-table.h"
35

    
36
namespace v8 {
37
namespace internal {
38

    
39

    
40
int Deoptimizer::patch_size() {
41
  const int kCallInstructionSizeInWords = 4;
42
  return kCallInstructionSizeInWords * Assembler::kInstrSize;
43
}
44

    
45

    
46
void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
47
  Address code_start_address = code->instruction_start();
48
  // Invalidate the relocation information, as it will become invalid by the
49
  // code patching below, and is not needed any more.
50
  code->InvalidateRelocation();
51

    
52
  // For each LLazyBailout instruction insert a call to the corresponding
53
  // deoptimization entry.
54
  DeoptimizationInputData* deopt_data =
55
      DeoptimizationInputData::cast(code->deoptimization_data());
56
#ifdef DEBUG
57
  Address prev_call_address = NULL;
58
#endif
59
  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
60
    if (deopt_data->Pc(i)->value() == -1) continue;
61
    Address call_address = code_start_address + deopt_data->Pc(i)->value();
62
    Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
63
    int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
64
                                                      RelocInfo::NONE32);
65
    int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
66
    ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0);
67
    ASSERT(call_size_in_bytes <= patch_size());
68
    CodePatcher patcher(call_address, call_size_in_words);
69
    patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
70
    ASSERT(prev_call_address == NULL ||
71
           call_address >= prev_call_address + patch_size());
72
    ASSERT(call_address + patch_size() <= code->instruction_end());
73

    
74
#ifdef DEBUG
75
    prev_call_address = call_address;
76
#endif
77
  }
78
}
79

    
80

    
81
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
82
  // Set the register values. The values are not important as there are no
83
  // callee saved registers in JavaScript frames, so all registers are
84
  // spilled. Registers fp and sp are set to the correct values though.
85

    
86
  for (int i = 0; i < Register::kNumRegisters; i++) {
87
    input_->SetRegister(i, i * 4);
88
  }
89
  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
90
  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
91
  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
92
    input_->SetDoubleRegister(i, 0.0);
93
  }
94

    
95
  // Fill the frame content from the actual data on the frame.
96
  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
97
    input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
98
  }
99
}
100

    
101

    
102
void Deoptimizer::SetPlatformCompiledStubRegisters(
103
    FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
104
  ApiFunction function(descriptor->deoptimization_handler_);
105
  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
106
  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
107
  int params = descriptor->environment_length();
108
  output_frame->SetRegister(s0.code(), params);
109
  output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize);
110
  output_frame->SetRegister(s2.code(), handler);
111
}
112

    
113

    
114
void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
115
  for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
116
    double double_value = input_->GetDoubleRegister(i);
117
    output_frame->SetDoubleRegister(i, double_value);
118
  }
119
}
120

    
121

    
122
bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
123
  // There is no dynamic alignment padding on MIPS in the input frame.
124
  return false;
125
}
126

    
127

    
128
#define __ masm()->
129

    
130

    
131
// This code tries to be close to ia32 code so that any changes can be
132
// easily ported.
133
void Deoptimizer::EntryGenerator::Generate() {
134
  GeneratePrologue();
135

    
136
  // Unlike on ARM we don't save all the registers, just the useful ones.
137
  // For the rest, there are gaps on the stack, so the offsets remain the same.
138
  const int kNumberOfRegisters = Register::kNumRegisters;
139

    
140
  RegList restored_regs = kJSCallerSaved | kCalleeSaved;
141
  RegList saved_regs = restored_regs | sp.bit() | ra.bit();
142

    
143
  const int kDoubleRegsSize =
144
      kDoubleSize * FPURegister::kMaxNumAllocatableRegisters;
145

    
146
  // Save all FPU registers before messing with them.
147
  __ Subu(sp, sp, Operand(kDoubleRegsSize));
148
  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
149
    FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
150
    int offset = i * kDoubleSize;
151
    __ sdc1(fpu_reg, MemOperand(sp, offset));
152
  }
153

    
154
  // Push saved_regs (needed to populate FrameDescription::registers_).
155
  // Leave gaps for other registers.
156
  __ Subu(sp, sp, kNumberOfRegisters * kPointerSize);
157
  for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
158
    if ((saved_regs & (1 << i)) != 0) {
159
      __ sw(ToRegister(i), MemOperand(sp, kPointerSize * i));
160
    }
161
  }
162

    
163
  const int kSavedRegistersAreaSize =
164
      (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
165

    
166
  // Get the bailout id from the stack.
167
  __ lw(a2, MemOperand(sp, kSavedRegistersAreaSize));
168

    
169
  // Get the address of the location in the code object (a3) (return
170
  // address for lazy deoptimization) and compute the fp-to-sp delta in
171
  // register t0.
172
  __ mov(a3, ra);
173
  // Correct one word for bailout id.
174
  __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
175

    
176
  __ Subu(t0, fp, t0);
177

    
178
  // Allocate a new deoptimizer object.
179
  // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
180
  __ PrepareCallCFunction(6, t1);
181
  __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
182
  __ li(a1, Operand(type()));  // bailout type,
183
  // a2: bailout id already loaded.
184
  // a3: code address or 0 already loaded.
185
  __ sw(t0, CFunctionArgumentOperand(5));  // Fp-to-sp delta.
186
  __ li(t1, Operand(ExternalReference::isolate_address(isolate())));
187
  __ sw(t1, CFunctionArgumentOperand(6));  // Isolate.
188
  // Call Deoptimizer::New().
189
  {
190
    AllowExternalCallThatCantCauseGC scope(masm());
191
    __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
192
  }
193

    
194
  // Preserve "deoptimizer" object in register v0 and get the input
195
  // frame descriptor pointer to a1 (deoptimizer->input_);
196
  // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
197
  __ mov(a0, v0);
198
  __ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
199

    
200
  // Copy core registers into FrameDescription::registers_[kNumRegisters].
201
  ASSERT(Register::kNumRegisters == kNumberOfRegisters);
202
  for (int i = 0; i < kNumberOfRegisters; i++) {
203
    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
204
    if ((saved_regs & (1 << i)) != 0) {
205
      __ lw(a2, MemOperand(sp, i * kPointerSize));
206
      __ sw(a2, MemOperand(a1, offset));
207
    } else if (FLAG_debug_code) {
208
      __ li(a2, kDebugZapValue);
209
      __ sw(a2, MemOperand(a1, offset));
210
    }
211
  }
212

    
213
  int double_regs_offset = FrameDescription::double_registers_offset();
214
  // Copy FPU registers to
215
  // double_registers_[DoubleRegister::kNumAllocatableRegisters]
216
  for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) {
217
    int dst_offset = i * kDoubleSize + double_regs_offset;
218
    int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
219
    __ ldc1(f0, MemOperand(sp, src_offset));
220
    __ sdc1(f0, MemOperand(a1, dst_offset));
221
  }
222

    
223
  // Remove the bailout id and the saved registers from the stack.
224
  __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
225

    
226
  // Compute a pointer to the unwinding limit in register a2; that is
227
  // the first stack slot not part of the input frame.
228
  __ lw(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
229
  __ Addu(a2, a2, sp);
230

    
231
  // Unwind the stack down to - but not including - the unwinding
232
  // limit and copy the contents of the activation frame to the input
233
  // frame description.
234
  __ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
235
  Label pop_loop;
236
  Label pop_loop_header;
237
  __ Branch(&pop_loop_header);
238
  __ bind(&pop_loop);
239
  __ pop(t0);
240
  __ sw(t0, MemOperand(a3, 0));
241
  __ addiu(a3, a3, sizeof(uint32_t));
242
  __ bind(&pop_loop_header);
243
  __ Branch(&pop_loop, ne, a2, Operand(sp));
244

    
245
  // Compute the output frame in the deoptimizer.
246
  __ push(a0);  // Preserve deoptimizer object across call.
247
  // a0: deoptimizer object; a1: scratch.
248
  __ PrepareCallCFunction(1, a1);
249
  // Call Deoptimizer::ComputeOutputFrames().
250
  {
251
    AllowExternalCallThatCantCauseGC scope(masm());
252
    __ CallCFunction(
253
        ExternalReference::compute_output_frames_function(isolate()), 1);
254
  }
255
  __ pop(a0);  // Restore deoptimizer object (class Deoptimizer).
256

    
257
  // Replace the current (input) frame with the output frames.
258
  Label outer_push_loop, inner_push_loop,
259
      outer_loop_header, inner_loop_header;
260
  // Outer loop state: t0 = current "FrameDescription** output_",
261
  // a1 = one past the last FrameDescription**.
262
  __ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
263
  __ lw(t0, MemOperand(a0, Deoptimizer::output_offset()));  // t0 is output_.
264
  __ sll(a1, a1, kPointerSizeLog2);  // Count to offset.
265
  __ addu(a1, t0, a1);  // a1 = one past the last FrameDescription**.
266
  __ jmp(&outer_loop_header);
267
  __ bind(&outer_push_loop);
268
  // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
269
  __ lw(a2, MemOperand(t0, 0));  // output_[ix]
270
  __ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
271
  __ jmp(&inner_loop_header);
272
  __ bind(&inner_push_loop);
273
  __ Subu(a3, a3, Operand(sizeof(uint32_t)));
274
  __ Addu(t2, a2, Operand(a3));
275
  __ lw(t3, MemOperand(t2, FrameDescription::frame_content_offset()));
276
  __ push(t3);
277
  __ bind(&inner_loop_header);
278
  __ Branch(&inner_push_loop, ne, a3, Operand(zero_reg));
279

    
280
  __ Addu(t0, t0, Operand(kPointerSize));
281
  __ bind(&outer_loop_header);
282
  __ Branch(&outer_push_loop, lt, t0, Operand(a1));
283

    
284
  __ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
285
  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
286
    const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
287
    int src_offset = i * kDoubleSize + double_regs_offset;
288
    __ ldc1(fpu_reg, MemOperand(a1, src_offset));
289
  }
290

    
291
  // Push state, pc, and continuation from the last output frame.
292
  __ lw(t2, MemOperand(a2, FrameDescription::state_offset()));
293
  __ push(t2);
294

    
295
  __ lw(t2, MemOperand(a2, FrameDescription::pc_offset()));
296
  __ push(t2);
297
  __ lw(t2, MemOperand(a2, FrameDescription::continuation_offset()));
298
  __ push(t2);
299

    
300

    
301
  // Technically restoring 'at' should work unless zero_reg is also restored
302
  // but it's safer to check for this.
303
  ASSERT(!(at.bit() & restored_regs));
304
  // Restore the registers from the last output frame.
305
  __ mov(at, a2);
306
  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
307
    int offset = (i * kPointerSize) + FrameDescription::registers_offset();
308
    if ((restored_regs & (1 << i)) != 0) {
309
      __ lw(ToRegister(i), MemOperand(at, offset));
310
    }
311
  }
312

    
313
  __ InitializeRootRegister();
314

    
315
  __ pop(at);  // Get continuation, leave pc on stack.
316
  __ pop(ra);
317
  __ Jump(at);
318
  __ stop("Unreachable.");
319
}
320

    
321

    
322
// Maximum size of a table entry generated below.
323
const int Deoptimizer::table_entry_size_ = 7 * Assembler::kInstrSize;
324

    
325
void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
326
  Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
327

    
328
  // Create a sequence of deoptimization entries.
329
  // Note that registers are still live when jumping to an entry.
330
  Label table_start;
331
  __ bind(&table_start);
332
  for (int i = 0; i < count(); i++) {
333
    Label start;
334
    __ bind(&start);
335
    __ addiu(sp, sp, -1 * kPointerSize);
336
    // Jump over the remaining deopt entries (including this one).
337
    // This code is always reached by calling Jump, which puts the target (label
338
    // start) into t9.
339
    const int remaining_entries = (count() - i) * table_entry_size_;
340
    __ Addu(t9, t9, remaining_entries);
341
    // 'at' was clobbered so we can only load the current entry value here.
342
    __ li(at, i);
343
    __ jr(t9);  // Expose delay slot.
344
    __ sw(at, MemOperand(sp, 0 * kPointerSize));  // In the delay slot.
345

    
346
    // Pad the rest of the code.
347
    while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) {
348
      __ nop();
349
    }
350

    
351
    ASSERT_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
352
  }
353

    
354
  ASSERT_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
355
      count() * table_entry_size_);
356
}
357

    
358

    
359
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
360
  SetFrameSlot(offset, value);
361
}
362

    
363

    
364
void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
365
  SetFrameSlot(offset, value);
366
}
367

    
368

    
369
#undef __
370

    
371

    
372
} }  // namespace v8::internal