V8 Project
deoptimizer-mips64.cc
Go to the documentation of this file.
1 // Copyright 2011 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 #include "src/codegen.h"
8 #include "src/deoptimizer.h"
9 #include "src/full-codegen.h"
10 #include "src/safepoint-table.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
17  const int kCallInstructionSizeInWords = 6;
18  return kCallInstructionSizeInWords * Assembler::kInstrSize;
19 }
20 
21 
22 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
23  Address code_start_address = code->instruction_start();
24  // Invalidate the relocation information, as it will become invalid by the
25  // code patching below, and is not needed any more.
26  code->InvalidateRelocation();
27 
28  if (FLAG_zap_code_space) {
29  // Fail hard and early if we enter this code object again.
30  byte* pointer = code->FindCodeAgeSequence();
31  if (pointer != NULL) {
32  pointer += kNoCodeAgeSequenceLength;
33  } else {
34  pointer = code->instruction_start();
35  }
36  CodePatcher patcher(pointer, 1);
37  patcher.masm()->break_(0xCC);
38 
39  DeoptimizationInputData* data =
40  DeoptimizationInputData::cast(code->deoptimization_data());
41  int osr_offset = data->OsrPcOffset()->value();
42  if (osr_offset > 0) {
43  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
44  osr_patcher.masm()->break_(0xCC);
45  }
46  }
47 
48  DeoptimizationInputData* deopt_data =
49  DeoptimizationInputData::cast(code->deoptimization_data());
50 #ifdef DEBUG
51  Address prev_call_address = NULL;
52 #endif
53  // For each LLazyBailout instruction insert a call to the corresponding
54  // deoptimization entry.
55  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
56  if (deopt_data->Pc(i)->value() == -1) continue;
57  Address call_address = code_start_address + deopt_data->Pc(i)->value();
58  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
59  int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
61  int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
62  DCHECK(call_size_in_bytes % Assembler::kInstrSize == 0);
63  DCHECK(call_size_in_bytes <= patch_size());
64  CodePatcher patcher(call_address, call_size_in_words);
65  patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
66  DCHECK(prev_call_address == NULL ||
67  call_address >= prev_call_address + patch_size());
68  DCHECK(call_address + patch_size() <= code->instruction_end());
69 
70 #ifdef DEBUG
71  prev_call_address = call_address;
72 #endif
73  }
74 }
75 
76 
77 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
78  // Set the register values. The values are not important as there are no
79  // callee saved registers in JavaScript frames, so all registers are
80  // spilled. Registers fp and sp are set to the correct values though.
81 
82  for (int i = 0; i < Register::kNumRegisters; i++) {
83  input_->SetRegister(i, i * 4);
84  }
85  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
86  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
87  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
88  input_->SetDoubleRegister(i, 0.0);
89  }
90 
91  // Fill the frame content from the actual data on the frame.
92  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
94  }
95 }
96 
97 
99  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
100  ApiFunction function(descriptor->deoptimization_handler());
101  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
102  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
103  int params = descriptor->GetHandlerParameterCount();
104  output_frame->SetRegister(s0.code(), params);
105  output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize);
106  output_frame->SetRegister(s2.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  __ Dsubu(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  __ ld(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 a4.
168  __ mov(a3, ra);
169  // Correct one word for bailout id.
170  __ Daddu(a4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
171 
172  __ Dsubu(a4, fp, a4);
173 
174  // Allocate a new deoptimizer object.
175  __ PrepareCallCFunction(6, a5);
176  // Pass six arguments, according to O32 or n64 ABI. a0..a3 are same for both.
177  __ li(a1, Operand(type())); // bailout type,
179  // a2: bailout id already loaded.
180  // a3: code address or 0 already loaded.
181  if (kMipsAbi == kN64) {
182  // a4: already has fp-to-sp delta.
183  __ li(a5, Operand(ExternalReference::isolate_address(isolate())));
184  } else { // O32 abi.
185  // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
186  __ sd(a4, CFunctionArgumentOperand(5)); // Fp-to-sp delta.
187  __ li(a5, Operand(ExternalReference::isolate_address(isolate())));
188  __ sd(a5, CFunctionArgumentOperand(6)); // Isolate.
189  }
190  // Call Deoptimizer::New().
191  {
192  AllowExternalCallThatCantCauseGC scope(masm());
193  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
194  }
195 
196  // Preserve "deoptimizer" object in register v0 and get the input
197  // frame descriptor pointer to a1 (deoptimizer->input_);
198  // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
199  __ mov(a0, v0);
200  __ ld(a1, MemOperand(v0, Deoptimizer::input_offset()));
201 
202  // Copy core registers into FrameDescription::registers_[kNumRegisters].
204  for (int i = 0; i < kNumberOfRegisters; i++) {
206  if ((saved_regs & (1 << i)) != 0) {
207  __ ld(a2, MemOperand(sp, i * kPointerSize));
208  __ sd(a2, MemOperand(a1, offset));
209  } else if (FLAG_debug_code) {
210  __ li(a2, kDebugZapValue);
211  __ sd(a2, MemOperand(a1, offset));
212  }
213  }
214 
215  int double_regs_offset = FrameDescription::double_registers_offset();
216  // Copy FPU registers to
217  // double_registers_[DoubleRegister::kNumAllocatableRegisters]
218  for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) {
219  int dst_offset = i * kDoubleSize + double_regs_offset;
220  int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
221  __ ldc1(f0, MemOperand(sp, src_offset));
222  __ sdc1(f0, MemOperand(a1, dst_offset));
223  }
224 
225  // Remove the bailout id and the saved registers from the stack.
226  __ Daddu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
227 
228  // Compute a pointer to the unwinding limit in register a2; that is
229  // the first stack slot not part of the input frame.
231  __ Daddu(a2, a2, sp);
232 
233  // Unwind the stack down to - but not including - the unwinding
234  // limit and copy the contents of the activation frame to the input
235  // frame description.
236  __ Daddu(a3, a1, Operand(FrameDescription::frame_content_offset()));
237  Label pop_loop;
238  Label pop_loop_header;
239  __ BranchShort(&pop_loop_header);
240  __ bind(&pop_loop);
241  __ pop(a4);
242  __ sd(a4, MemOperand(a3, 0));
243  __ daddiu(a3, a3, sizeof(uint64_t));
244  __ bind(&pop_loop_header);
245  __ BranchShort(&pop_loop, ne, a2, Operand(sp));
246  // Compute the output frame in the deoptimizer.
247  __ push(a0); // Preserve deoptimizer object across call.
248  // a0: deoptimizer object; a1: scratch.
249  __ PrepareCallCFunction(1, a1);
250  // Call Deoptimizer::ComputeOutputFrames().
251  {
252  AllowExternalCallThatCantCauseGC scope(masm());
253  __ CallCFunction(
254  ExternalReference::compute_output_frames_function(isolate()), 1);
255  }
256  __ pop(a0); // Restore deoptimizer object (class Deoptimizer).
257 
258  // Replace the current (input) frame with the output frames.
259  Label outer_push_loop, inner_push_loop,
260  outer_loop_header, inner_loop_header;
261  // Outer loop state: a4 = current "FrameDescription** output_",
262  // a1 = one past the last FrameDescription**.
264  __ ld(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_.
265  __ dsll(a1, a1, kPointerSizeLog2); // Count to offset.
266  __ daddu(a1, a4, a1); // a1 = one past the last FrameDescription**.
267  __ jmp(&outer_loop_header);
268  __ bind(&outer_push_loop);
269  // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
270  __ ld(a2, MemOperand(a4, 0)); // output_[ix]
272  __ jmp(&inner_loop_header);
273  __ bind(&inner_push_loop);
274  __ Dsubu(a3, a3, Operand(sizeof(uint64_t)));
275  __ Daddu(a6, a2, Operand(a3));
277  __ push(a7);
278  __ bind(&inner_loop_header);
279  __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
280 
281  __ Daddu(a4, a4, Operand(kPointerSize));
282  __ bind(&outer_loop_header);
283  __ BranchShort(&outer_push_loop, lt, a4, Operand(a1));
284 
285  __ ld(a1, MemOperand(a0, Deoptimizer::input_offset()));
286  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
287  const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
288  int src_offset = i * kDoubleSize + double_regs_offset;
289  __ ldc1(fpu_reg, MemOperand(a1, src_offset));
290  }
291 
292  // Push state, pc, and continuation from the last output frame.
294  __ push(a6);
295 
297  __ push(a6);
299  __ push(a6);
300 
301 
302  // Technically restoring 'at' should work unless zero_reg is also restored
303  // but it's safer to check for this.
304  DCHECK(!(at.bit() & restored_regs));
305  // Restore the registers from the last output frame.
306  __ mov(at, a2);
307  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
309  if ((restored_regs & (1 << i)) != 0) {
310  __ ld(ToRegister(i), MemOperand(at, offset));
311  }
312  }
313 
314  __ InitializeRootRegister();
315 
316  __ pop(at); // Get continuation, leave pc on stack.
317  __ pop(ra);
318  __ Jump(at);
319  __ stop("Unreachable.");
320 }
321 
322 
323 // Maximum size of a table entry generated below.
325 
327  Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
328 
329  // Create a sequence of deoptimization entries.
330  // Note that registers are still live when jumping to an entry.
331  Label table_start;
332  __ bind(&table_start);
333  for (int i = 0; i < count(); i++) {
334  Label start;
335  __ bind(&start);
336  __ daddiu(sp, sp, -1 * kPointerSize);
337  // Jump over the remaining deopt entries (including this one).
338  // This code is always reached by calling Jump, which puts the target (label
339  // start) into t9.
340  const int remaining_entries = (count() - i) * table_entry_size_;
341  __ Daddu(t9, t9, remaining_entries);
342  // 'at' was clobbered so we can only load the current entry value here.
343  __ li(t8, i);
344  __ jr(t9); // Expose delay slot.
345  __ sd(t8, MemOperand(sp, 0 * kPointerSize)); // In the delay slot.
346 
347  // Pad the rest of the code.
348  while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) {
349  __ nop();
350  }
351 
352  DCHECK_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
353  }
354 
355  DCHECK_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
357 }
358 
359 
360 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
361  SetFrameSlot(offset, value);
362 }
363 
364 
365 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
366  SetFrameSlot(offset, value);
367 }
368 
369 
370 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
371  // No out-of-line constant pool support.
372  UNREACHABLE();
373 }
374 
375 
376 #undef __
377 
378 
379 } } // 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 uint64_t & uint64_at(Address addr)
Definition: v8memory.h:32
static const AbiVariants kMipsAbi
@ kN64
#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
const int kPointerSize
Definition: globals.h:129
const uint32_t kDebugZapValue
Definition: globals.h:274
const RegList kJSCallerSaved
Definition: frames-arm.h:24
const SwVfpRegister s1
const RegList kCalleeSaved
Definition: frames-arm.h:38
const SwVfpRegister s2
const int kDoubleSize
Definition: globals.h:127
const SwVfpRegister s0
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