V8 Project
deoptimizer-mips.cc
Go to the documentation of this file.
1 
2 // Copyright 2011 the V8 project authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "src/v8.h"
7 
8 #include "src/codegen.h"
9 #include "src/deoptimizer.h"
10 #include "src/full-codegen.h"
11 #include "src/safepoint-table.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 
18  const int kCallInstructionSizeInWords = 4;
19  return kCallInstructionSizeInWords * Assembler::kInstrSize;
20 }
21 
22 
23 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
24  Address code_start_address = code->instruction_start();
25  // Invalidate the relocation information, as it will become invalid by the
26  // code patching below, and is not needed any more.
27  code->InvalidateRelocation();
28 
29  if (FLAG_zap_code_space) {
30  // Fail hard and early if we enter this code object again.
31  byte* pointer = code->FindCodeAgeSequence();
32  if (pointer != NULL) {
33  pointer += kNoCodeAgeSequenceLength;
34  } else {
35  pointer = code->instruction_start();
36  }
37  CodePatcher patcher(pointer, 1);
38  patcher.masm()->break_(0xCC);
39 
40  DeoptimizationInputData* data =
41  DeoptimizationInputData::cast(code->deoptimization_data());
42  int osr_offset = data->OsrPcOffset()->value();
43  if (osr_offset > 0) {
44  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
45  osr_patcher.masm()->break_(0xCC);
46  }
47  }
48 
49  DeoptimizationInputData* deopt_data =
50  DeoptimizationInputData::cast(code->deoptimization_data());
51 #ifdef DEBUG
52  Address prev_call_address = NULL;
53 #endif
54  // For each LLazyBailout instruction insert a call to the corresponding
55  // deoptimization entry.
56  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
57  if (deopt_data->Pc(i)->value() == -1) continue;
58  Address call_address = code_start_address + deopt_data->Pc(i)->value();
59  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
60  int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
62  int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
63  DCHECK(call_size_in_bytes % Assembler::kInstrSize == 0);
64  DCHECK(call_size_in_bytes <= patch_size());
65  CodePatcher patcher(call_address, call_size_in_words);
66  patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
67  DCHECK(prev_call_address == NULL ||
68  call_address >= prev_call_address + patch_size());
69  DCHECK(call_address + patch_size() <= code->instruction_end());
70 
71 #ifdef DEBUG
72  prev_call_address = call_address;
73 #endif
74  }
75 }
76 
77 
78 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
79  // Set the register values. The values are not important as there are no
80  // callee saved registers in JavaScript frames, so all registers are
81  // spilled. Registers fp and sp are set to the correct values though.
82 
83  for (int i = 0; i < Register::kNumRegisters; i++) {
84  input_->SetRegister(i, i * 4);
85  }
86  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
87  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
88  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
89  input_->SetDoubleRegister(i, 0.0);
90  }
91 
92  // Fill the frame content from the actual data on the frame.
93  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
95  }
96 }
97 
98 
100  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
101  ApiFunction function(descriptor->deoptimization_handler());
102  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
103  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
104  int params = descriptor->GetHandlerParameterCount();
105  output_frame->SetRegister(a0.code(), params);
106  output_frame->SetRegister(a1.code(), handler);
107 }
108 
109 
110 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
111  for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
112  double double_value = input_->GetDoubleRegister(i);
113  output_frame->SetDoubleRegister(i, double_value);
114  }
115 }
116 
117 
118 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
119  // There is no dynamic alignment padding on MIPS in the input frame.
120  return false;
121 }
122 
123 
124 #define __ masm()->
125 
126 
127 // This code tries to be close to ia32 code so that any changes can be
128 // easily ported.
129 void Deoptimizer::EntryGenerator::Generate() {
130  GeneratePrologue();
131 
132  // Unlike on ARM we don't save all the registers, just the useful ones.
133  // For the rest, there are gaps on the stack, so the offsets remain the same.
135 
136  RegList restored_regs = kJSCallerSaved | kCalleeSaved;
137  RegList saved_regs = restored_regs | sp.bit() | ra.bit();
138 
139  const int kDoubleRegsSize =
141 
142  // Save all FPU registers before messing with them.
143  __ Subu(sp, sp, Operand(kDoubleRegsSize));
144  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
145  FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
146  int offset = i * kDoubleSize;
147  __ sdc1(fpu_reg, MemOperand(sp, offset));
148  }
149 
150  // Push saved_regs (needed to populate FrameDescription::registers_).
151  // Leave gaps for other registers.
153  for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
154  if ((saved_regs & (1 << i)) != 0) {
156  }
157  }
158 
159  const int kSavedRegistersAreaSize =
160  (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
161 
162  // Get the bailout id from the stack.
163  __ lw(a2, MemOperand(sp, kSavedRegistersAreaSize));
164 
165  // Get the address of the location in the code object (a3) (return
166  // address for lazy deoptimization) and compute the fp-to-sp delta in
167  // register t0.
168  __ mov(a3, ra);
169  // Correct one word for bailout id.
170  __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
171 
172  __ Subu(t0, fp, t0);
173 
174  // Allocate a new deoptimizer object.
175  // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
176  __ PrepareCallCFunction(6, t1);
178  __ li(a1, Operand(type())); // bailout type,
179  // a2: bailout id already loaded.
180  // a3: code address or 0 already loaded.
181  __ sw(t0, CFunctionArgumentOperand(5)); // Fp-to-sp delta.
182  __ li(t1, Operand(ExternalReference::isolate_address(isolate())));
183  __ sw(t1, CFunctionArgumentOperand(6)); // Isolate.
184  // Call Deoptimizer::New().
185  {
186  AllowExternalCallThatCantCauseGC scope(masm());
187  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
188  }
189 
190  // Preserve "deoptimizer" object in register v0 and get the input
191  // frame descriptor pointer to a1 (deoptimizer->input_);
192  // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
193  __ mov(a0, v0);
194  __ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
195 
196  // Copy core registers into FrameDescription::registers_[kNumRegisters].
198  for (int i = 0; i < kNumberOfRegisters; i++) {
200  if ((saved_regs & (1 << i)) != 0) {
201  __ lw(a2, MemOperand(sp, i * kPointerSize));
202  __ sw(a2, MemOperand(a1, offset));
203  } else if (FLAG_debug_code) {
204  __ li(a2, kDebugZapValue);
205  __ sw(a2, MemOperand(a1, offset));
206  }
207  }
208 
209  int double_regs_offset = FrameDescription::double_registers_offset();
210  // Copy FPU registers to
211  // double_registers_[DoubleRegister::kNumAllocatableRegisters]
212  for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) {
213  int dst_offset = i * kDoubleSize + double_regs_offset;
214  int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
215  __ ldc1(f0, MemOperand(sp, src_offset));
216  __ sdc1(f0, MemOperand(a1, dst_offset));
217  }
218 
219  // Remove the bailout id and the saved registers from the stack.
220  __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
221 
222  // Compute a pointer to the unwinding limit in register a2; that is
223  // the first stack slot not part of the input frame.
225  __ Addu(a2, a2, sp);
226 
227  // Unwind the stack down to - but not including - the unwinding
228  // limit and copy the contents of the activation frame to the input
229  // frame description.
230  __ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
231  Label pop_loop;
232  Label pop_loop_header;
233  __ BranchShort(&pop_loop_header);
234  __ bind(&pop_loop);
235  __ pop(t0);
236  __ sw(t0, MemOperand(a3, 0));
237  __ addiu(a3, a3, sizeof(uint32_t));
238  __ bind(&pop_loop_header);
239  __ BranchShort(&pop_loop, ne, a2, Operand(sp));
240 
241  // Compute the output frame in the deoptimizer.
242  __ push(a0); // Preserve deoptimizer object across call.
243  // a0: deoptimizer object; a1: scratch.
244  __ PrepareCallCFunction(1, a1);
245  // Call Deoptimizer::ComputeOutputFrames().
246  {
247  AllowExternalCallThatCantCauseGC scope(masm());
248  __ CallCFunction(
249  ExternalReference::compute_output_frames_function(isolate()), 1);
250  }
251  __ pop(a0); // Restore deoptimizer object (class Deoptimizer).
252 
253  // Replace the current (input) frame with the output frames.
254  Label outer_push_loop, inner_push_loop,
255  outer_loop_header, inner_loop_header;
256  // Outer loop state: t0 = current "FrameDescription** output_",
257  // a1 = one past the last FrameDescription**.
259  __ lw(t0, MemOperand(a0, Deoptimizer::output_offset())); // t0 is output_.
260  __ sll(a1, a1, kPointerSizeLog2); // Count to offset.
261  __ addu(a1, t0, a1); // a1 = one past the last FrameDescription**.
262  __ jmp(&outer_loop_header);
263  __ bind(&outer_push_loop);
264  // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
265  __ lw(a2, MemOperand(t0, 0)); // output_[ix]
267  __ jmp(&inner_loop_header);
268  __ bind(&inner_push_loop);
269  __ Subu(a3, a3, Operand(sizeof(uint32_t)));
270  __ Addu(t2, a2, Operand(a3));
272  __ push(t3);
273  __ bind(&inner_loop_header);
274  __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
275 
276  __ Addu(t0, t0, Operand(kPointerSize));
277  __ bind(&outer_loop_header);
278  __ BranchShort(&outer_push_loop, lt, t0, Operand(a1));
279 
280  __ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
281  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
282  const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
283  int src_offset = i * kDoubleSize + double_regs_offset;
284  __ ldc1(fpu_reg, MemOperand(a1, src_offset));
285  }
286 
287  // Push state, pc, and continuation from the last output frame.
289  __ push(t2);
290 
292  __ push(t2);
294  __ push(t2);
295 
296 
297  // Technically restoring 'at' should work unless zero_reg is also restored
298  // but it's safer to check for this.
299  DCHECK(!(at.bit() & restored_regs));
300  // Restore the registers from the last output frame.
301  __ mov(at, a2);
302  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
304  if ((restored_regs & (1 << i)) != 0) {
305  __ lw(ToRegister(i), MemOperand(at, offset));
306  }
307  }
308 
309  __ InitializeRootRegister();
310 
311  __ pop(at); // Get continuation, leave pc on stack.
312  __ pop(ra);
313  __ Jump(at);
314  __ stop("Unreachable.");
315 }
316 
317 
318 // Maximum size of a table entry generated below.
320 
322  Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
323 
324  // Create a sequence of deoptimization entries.
325  // Note that registers are still live when jumping to an entry.
326  Label table_start, done, done_special, trampoline_jump;
327  __ bind(&table_start);
328  int kMaxEntriesBranchReach = (1 << (kImm16Bits - 2))/
330 
331  if (count() <= kMaxEntriesBranchReach) {
332  // Common case.
333  for (int i = 0; i < count(); i++) {
334  Label start;
335  __ bind(&start);
336  DCHECK(is_int16(i));
337  __ Branch(USE_DELAY_SLOT, &done); // Expose delay slot.
338  __ li(at, i); // In the delay slot.
339 
340  DCHECK_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
341  }
342 
343  DCHECK_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
345  __ bind(&done);
346  __ Push(at);
347  } else {
348  // Uncommon case, the branch cannot reach.
349  // Create mini trampoline and adjust id constants to get proper value at
350  // the end of table.
351  for (int i = kMaxEntriesBranchReach; i > 1; i--) {
352  Label start;
353  __ bind(&start);
354  DCHECK(is_int16(i));
355  __ Branch(USE_DELAY_SLOT, &trampoline_jump); // Expose delay slot.
356  __ li(at, - i); // In the delay slot.
357  DCHECK_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
358  }
359  // Entry with id == kMaxEntriesBranchReach - 1.
360  __ bind(&trampoline_jump);
361  __ Branch(USE_DELAY_SLOT, &done_special);
362  __ li(at, -1);
363 
364  for (int i = kMaxEntriesBranchReach ; i < count(); i++) {
365  Label start;
366  __ bind(&start);
367  DCHECK(is_int16(i));
368  __ Branch(USE_DELAY_SLOT, &done); // Expose delay slot.
369  __ li(at, i); // In the delay slot.
370  }
371 
372  DCHECK_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
374  __ bind(&done_special);
375  __ addiu(at, at, kMaxEntriesBranchReach);
376  __ bind(&done);
377  __ Push(at);
378  }
379 }
380 
381 
382 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
383  SetFrameSlot(offset, value);
384 }
385 
386 
387 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
388  SetFrameSlot(offset, value);
389 }
390 
391 
392 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
393  // No out-of-line constant pool support.
394  UNREACHABLE();
395 }
396 
397 
398 #undef __
399 
400 
401 } } // namespace v8::internal
static const int kInstrSize
friend class BlockTrampolinePoolScope
static int output_offset()
Definition: deoptimizer.h:239
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)
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
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
static int CallSize(Register target, Condition cond=al)
static uint32_t & uint32_at(Address addr)
Definition: v8memory.h:24
#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(condition)
Definition: logging.h:205
#define DCHECK_EQ(v1, v2)
Definition: logging.h:206
signed short int16_t
Definition: unicode.cc:22
static int Push(SpecialRPOStackFrame *stack, int depth, BasicBlock *child, int unvisited)
Definition: scheduler.cc:773
const int kPointerSize
Definition: globals.h:129
const uint32_t kDebugZapValue
Definition: globals.h:274
const int kImm16Bits
const RegList kJSCallerSaved
Definition: frames-arm.h:24
const RegList kCalleeSaved
Definition: frames-arm.h:38
const int kDoubleSize
Definition: globals.h:127
const Register fp
const Register sp
const int kPointerSizeLog2
Definition: globals.h:147
MemOperand CFunctionArgumentOperand(int index)
uint32_t RegList
Definition: frames.h:18
byte * Address
Definition: globals.h:101
Register ToRegister(int num)
const unsigned kNumberOfRegisters
static const int kNoCodeAgeSequenceLength
const FPURegister f0
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
static const int kMaxNumRegisters
static FPURegister FromAllocationIndex(int index)
static const int kMaxNumAllocatableRegisters
static int NumAllocatableRegisters()
static const int kNumRegisters
Definition: assembler-arm.h:95