v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
deoptimizer-mips.cc
Go to the documentation of this file.
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 
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  if (FLAG_zap_code_space) {
53  // Fail hard and early if we enter this code object again.
54  byte* pointer = code->FindCodeAgeSequence();
55  if (pointer != NULL) {
56  pointer += kNoCodeAgeSequenceLength * Assembler::kInstrSize;
57  } else {
58  pointer = code->instruction_start();
59  }
60  CodePatcher patcher(pointer, 1);
61  patcher.masm()->break_(0xCC);
62 
63  DeoptimizationInputData* data =
64  DeoptimizationInputData::cast(code->deoptimization_data());
65  int osr_offset = data->OsrPcOffset()->value();
66  if (osr_offset > 0) {
67  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
68  osr_patcher.masm()->break_(0xCC);
69  }
70  }
71 
72  DeoptimizationInputData* deopt_data =
73  DeoptimizationInputData::cast(code->deoptimization_data());
74  SharedFunctionInfo* shared =
75  SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
76  shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
77 #ifdef DEBUG
78  Address prev_call_address = NULL;
79 #endif
80  // For each LLazyBailout instruction insert a call to the corresponding
81  // deoptimization entry.
82  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
83  if (deopt_data->Pc(i)->value() == -1) continue;
84  Address call_address = code_start_address + deopt_data->Pc(i)->value();
85  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
86  int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
87  RelocInfo::NONE32);
88  int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
89  ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0);
90  ASSERT(call_size_in_bytes <= patch_size());
91  CodePatcher patcher(call_address, call_size_in_words);
92  patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
93  ASSERT(prev_call_address == NULL ||
94  call_address >= prev_call_address + patch_size());
95  ASSERT(call_address + patch_size() <= code->instruction_end());
96 
97 #ifdef DEBUG
98  prev_call_address = call_address;
99 #endif
100  }
101 }
102 
103 
104 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
105  // Set the register values. The values are not important as there are no
106  // callee saved registers in JavaScript frames, so all registers are
107  // spilled. Registers fp and sp are set to the correct values though.
108 
109  for (int i = 0; i < Register::kNumRegisters; i++) {
110  input_->SetRegister(i, i * 4);
111  }
112  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
113  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
114  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
115  input_->SetDoubleRegister(i, 0.0);
116  }
117 
118  // Fill the frame content from the actual data on the frame.
119  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
120  input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
121  }
122 }
123 
124 
125 void Deoptimizer::SetPlatformCompiledStubRegisters(
126  FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
127  ApiFunction function(descriptor->deoptimization_handler_);
128  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
129  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
130  int params = descriptor->GetHandlerParameterCount();
131  output_frame->SetRegister(s0.code(), params);
132  output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize);
133  output_frame->SetRegister(s2.code(), handler);
134 }
135 
136 
137 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
138  for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
139  double double_value = input_->GetDoubleRegister(i);
140  output_frame->SetDoubleRegister(i, double_value);
141  }
142 }
143 
144 
145 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
146  // There is no dynamic alignment padding on MIPS in the input frame.
147  return false;
148 }
149 
150 
151 Code* Deoptimizer::NotifyStubFailureBuiltin() {
152  return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
153 }
154 
155 
156 #define __ masm()->
157 
158 
159 // This code tries to be close to ia32 code so that any changes can be
160 // easily ported.
161 void Deoptimizer::EntryGenerator::Generate() {
162  GeneratePrologue();
163 
164  // Unlike on ARM we don't save all the registers, just the useful ones.
165  // For the rest, there are gaps on the stack, so the offsets remain the same.
167 
168  RegList restored_regs = kJSCallerSaved | kCalleeSaved;
169  RegList saved_regs = restored_regs | sp.bit() | ra.bit();
170 
171  const int kDoubleRegsSize =
173 
174  // Save all FPU registers before messing with them.
175  __ Subu(sp, sp, Operand(kDoubleRegsSize));
176  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
177  FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
178  int offset = i * kDoubleSize;
179  __ sdc1(fpu_reg, MemOperand(sp, offset));
180  }
181 
182  // Push saved_regs (needed to populate FrameDescription::registers_).
183  // Leave gaps for other registers.
184  __ Subu(sp, sp, kNumberOfRegisters * kPointerSize);
185  for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
186  if ((saved_regs & (1 << i)) != 0) {
187  __ sw(ToRegister(i), MemOperand(sp, kPointerSize * i));
188  }
189  }
190 
191  const int kSavedRegistersAreaSize =
192  (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
193 
194  // Get the bailout id from the stack.
195  __ lw(a2, MemOperand(sp, kSavedRegistersAreaSize));
196 
197  // Get the address of the location in the code object (a3) (return
198  // address for lazy deoptimization) and compute the fp-to-sp delta in
199  // register t0.
200  __ mov(a3, ra);
201  // Correct one word for bailout id.
202  __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
203 
204  __ Subu(t0, fp, t0);
205 
206  // Allocate a new deoptimizer object.
207  // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
208  __ PrepareCallCFunction(6, t1);
210  __ li(a1, Operand(type())); // bailout type,
211  // a2: bailout id already loaded.
212  // a3: code address or 0 already loaded.
213  __ sw(t0, CFunctionArgumentOperand(5)); // Fp-to-sp delta.
214  __ li(t1, Operand(ExternalReference::isolate_address(isolate())));
215  __ sw(t1, CFunctionArgumentOperand(6)); // Isolate.
216  // Call Deoptimizer::New().
217  {
218  AllowExternalCallThatCantCauseGC scope(masm());
219  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
220  }
221 
222  // Preserve "deoptimizer" object in register v0 and get the input
223  // frame descriptor pointer to a1 (deoptimizer->input_);
224  // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
225  __ mov(a0, v0);
226  __ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
227 
228  // Copy core registers into FrameDescription::registers_[kNumRegisters].
229  ASSERT(Register::kNumRegisters == kNumberOfRegisters);
230  for (int i = 0; i < kNumberOfRegisters; i++) {
231  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
232  if ((saved_regs & (1 << i)) != 0) {
233  __ lw(a2, MemOperand(sp, i * kPointerSize));
234  __ sw(a2, MemOperand(a1, offset));
235  } else if (FLAG_debug_code) {
236  __ li(a2, kDebugZapValue);
237  __ sw(a2, MemOperand(a1, offset));
238  }
239  }
240 
241  int double_regs_offset = FrameDescription::double_registers_offset();
242  // Copy FPU registers to
243  // double_registers_[DoubleRegister::kNumAllocatableRegisters]
244  for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) {
245  int dst_offset = i * kDoubleSize + double_regs_offset;
246  int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
247  __ ldc1(f0, MemOperand(sp, src_offset));
248  __ sdc1(f0, MemOperand(a1, dst_offset));
249  }
250 
251  // Remove the bailout id and the saved registers from the stack.
252  __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
253 
254  // Compute a pointer to the unwinding limit in register a2; that is
255  // the first stack slot not part of the input frame.
257  __ Addu(a2, a2, sp);
258 
259  // Unwind the stack down to - but not including - the unwinding
260  // limit and copy the contents of the activation frame to the input
261  // frame description.
262  __ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
263  Label pop_loop;
264  Label pop_loop_header;
265  __ BranchShort(&pop_loop_header);
266  __ bind(&pop_loop);
267  __ pop(t0);
268  __ sw(t0, MemOperand(a3, 0));
269  __ addiu(a3, a3, sizeof(uint32_t));
270  __ bind(&pop_loop_header);
271  __ BranchShort(&pop_loop, ne, a2, Operand(sp));
272 
273  // Compute the output frame in the deoptimizer.
274  __ push(a0); // Preserve deoptimizer object across call.
275  // a0: deoptimizer object; a1: scratch.
276  __ PrepareCallCFunction(1, a1);
277  // Call Deoptimizer::ComputeOutputFrames().
278  {
279  AllowExternalCallThatCantCauseGC scope(masm());
280  __ CallCFunction(
281  ExternalReference::compute_output_frames_function(isolate()), 1);
282  }
283  __ pop(a0); // Restore deoptimizer object (class Deoptimizer).
284 
285  // Replace the current (input) frame with the output frames.
286  Label outer_push_loop, inner_push_loop,
287  outer_loop_header, inner_loop_header;
288  // Outer loop state: t0 = current "FrameDescription** output_",
289  // a1 = one past the last FrameDescription**.
291  __ lw(t0, MemOperand(a0, Deoptimizer::output_offset())); // t0 is output_.
292  __ sll(a1, a1, kPointerSizeLog2); // Count to offset.
293  __ addu(a1, t0, a1); // a1 = one past the last FrameDescription**.
294  __ jmp(&outer_loop_header);
295  __ bind(&outer_push_loop);
296  // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
297  __ lw(a2, MemOperand(t0, 0)); // output_[ix]
299  __ jmp(&inner_loop_header);
300  __ bind(&inner_push_loop);
301  __ Subu(a3, a3, Operand(sizeof(uint32_t)));
302  __ Addu(t2, a2, Operand(a3));
304  __ push(t3);
305  __ bind(&inner_loop_header);
306  __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
307 
308  __ Addu(t0, t0, Operand(kPointerSize));
309  __ bind(&outer_loop_header);
310  __ BranchShort(&outer_push_loop, lt, t0, Operand(a1));
311 
312  __ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
313  for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
314  const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
315  int src_offset = i * kDoubleSize + double_regs_offset;
316  __ ldc1(fpu_reg, MemOperand(a1, src_offset));
317  }
318 
319  // Push state, pc, and continuation from the last output frame.
321  __ push(t2);
322 
324  __ push(t2);
326  __ push(t2);
327 
328 
329  // Technically restoring 'at' should work unless zero_reg is also restored
330  // but it's safer to check for this.
331  ASSERT(!(at.bit() & restored_regs));
332  // Restore the registers from the last output frame.
333  __ mov(at, a2);
334  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
335  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
336  if ((restored_regs & (1 << i)) != 0) {
337  __ lw(ToRegister(i), MemOperand(at, offset));
338  }
339  }
340 
341  __ InitializeRootRegister();
342 
343  __ pop(at); // Get continuation, leave pc on stack.
344  __ pop(ra);
345  __ Jump(at);
346  __ stop("Unreachable.");
347 }
348 
349 
350 // Maximum size of a table entry generated below.
351 const int Deoptimizer::table_entry_size_ = 7 * Assembler::kInstrSize;
352 
354  Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
355 
356  // Create a sequence of deoptimization entries.
357  // Note that registers are still live when jumping to an entry.
358  Label table_start;
359  __ bind(&table_start);
360  for (int i = 0; i < count(); i++) {
361  Label start;
362  __ bind(&start);
363  __ addiu(sp, sp, -1 * kPointerSize);
364  // Jump over the remaining deopt entries (including this one).
365  // This code is always reached by calling Jump, which puts the target (label
366  // start) into t9.
367  const int remaining_entries = (count() - i) * table_entry_size_;
368  __ Addu(t9, t9, remaining_entries);
369  // 'at' was clobbered so we can only load the current entry value here.
370  __ li(at, i);
371  __ jr(t9); // Expose delay slot.
372  __ sw(at, MemOperand(sp, 0 * kPointerSize)); // In the delay slot.
373 
374  // Pad the rest of the code.
375  while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) {
376  __ nop();
377  }
378 
379  ASSERT_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
380  }
381 
382  ASSERT_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
383  count() * table_entry_size_);
384 }
385 
386 
387 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
388  SetFrameSlot(offset, value);
389 }
390 
391 
392 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
393  SetFrameSlot(offset, value);
394 }
395 
396 
397 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
398  // No out-of-line constant pool support.
399  UNREACHABLE();
400 }
401 
402 
403 #undef __
404 
405 
406 } } // namespace v8::internal
byte * Address
Definition: globals.h:186
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
Definition: flags.cc:269
const SwVfpRegister s2
void SetCallerPc(unsigned offset, intptr_t value)
Code * builtin(Name name)
Definition: builtins.h:322
const FPURegister f0
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:503
Builtins * builtins()
Definition: isolate.h:948
uint32_t RegList
Definition: frames.h:41
#define ASSERT(condition)
Definition: checks.h:329
const RegList kJSCallerSaved
Definition: frames-arm.h:47
const int kPointerSizeLog2
Definition: globals.h:281
static SharedFunctionInfo * cast(Object *obj)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:526
static const int kMaxNumAllocatableRegisters
static const int kNumRegisters
static int double_registers_offset()
Definition: deoptimizer.h:582
uint8_t byte
Definition: globals.h:185
const Register sp
#define UNREACHABLE()
Definition: checks.h:52
static int output_offset()
Definition: deoptimizer.h:250
const int kDoubleSize
Definition: globals.h:266
static Address GetDeoptimizationEntry(Isolate *isolate, int id, BailoutType type, GetEntryMode mode=ENSURE_ENTRY_CODE)
Definition: deoptimizer.cc:683
const int kPointerSize
Definition: globals.h:268
const RegList kCalleeSaved
Definition: frames-arm.h:63
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra code(assertions) for debugging") DEFINE_bool(code_comments
void SetRegister(unsigned n, intptr_t value)
Definition: deoptimizer.h:531
static const int kMaxNumRegisters
static int NumAllocatableRegisters()
const SwVfpRegister s0
static int CallSize(Register target, Condition cond=al)
friend class BlockTrampolinePoolScope
void SetCallerConstantPool(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:485
const SwVfpRegister s1
static int output_count_offset()
Definition: deoptimizer.h:247
const unsigned kNumberOfRegisters
MemOperand CFunctionArgumentOperand(int index)
#define ASSERT_EQ(v1, v2)
Definition: checks.h:330
virtual void GeneratePrologue()
#define __
const uint32_t kDebugZapValue
Definition: v8globals.h:87
static const int kInstrSize
static uint32_t & uint32_at(Address addr)
Definition: v8memory.h:47
const Register fp
static FPURegister FromAllocationIndex(int index)
signed short int16_t
Definition: unicode.cc:45
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:536
Register ToRegister(int num)
void SetCallerFp(unsigned offset, intptr_t value)
void EvictFromOptimizedCodeMap(Code *optimized_code, const char *reason)
Definition: objects.cc:9593
static DeoptimizationInputData * cast(Object *obj)