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-arm64.cc
Go to the documentation of this file.
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include "v8.h"
29 
30 #include "codegen.h"
31 #include "deoptimizer.h"
32 #include "full-codegen.h"
33 #include "safepoint-table.h"
34 
35 
36 namespace v8 {
37 namespace internal {
38 
39 
41  // Size of the code used to patch lazy bailout points.
42  // Patching is done by Deoptimizer::DeoptimizeFunction.
43  return 4 * kInstructionSize;
44 }
45 
46 
47 
48 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
49  // Invalidate the relocation information, as it will become invalid by the
50  // code patching below, and is not needed any more.
51  code->InvalidateRelocation();
52 
53  // TODO(jkummerow): if (FLAG_zap_code_space), make the code object's
54  // entry sequence unusable (see other architectures).
55 
56  DeoptimizationInputData* deopt_data =
57  DeoptimizationInputData::cast(code->deoptimization_data());
58  SharedFunctionInfo* shared =
59  SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
60  shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
61  Address code_start_address = code->instruction_start();
62 #ifdef DEBUG
63  Address prev_call_address = NULL;
64 #endif
65  // For each LLazyBailout instruction insert a call to the corresponding
66  // deoptimization entry.
67  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
68  if (deopt_data->Pc(i)->value() == -1) continue;
69 
70  Address call_address = code_start_address + deopt_data->Pc(i)->value();
71  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
72 
73  PatchingAssembler patcher(call_address, patch_size() / kInstructionSize);
74  patcher.LoadLiteral(ip0, 2 * kInstructionSize);
75  patcher.blr(ip0);
76  patcher.dc64(reinterpret_cast<intptr_t>(deopt_entry));
77 
78  ASSERT((prev_call_address == NULL) ||
79  (call_address >= prev_call_address + patch_size()));
80  ASSERT(call_address + patch_size() <= code->instruction_end());
81 #ifdef DEBUG
82  prev_call_address = call_address;
83 #endif
84  }
85 }
86 
87 
88 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
89  // Set the register values. The values are not important as there are no
90  // callee saved registers in JavaScript frames, so all registers are
91  // spilled. Registers fp and sp are set to the correct values though.
92  for (int i = 0; i < Register::NumRegisters(); i++) {
93  input_->SetRegister(i, 0);
94  }
95 
96  // TODO(all): Do we also need to set a value to csp?
97  input_->SetRegister(jssp.code(), reinterpret_cast<intptr_t>(frame->sp()));
98  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
99 
100  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
101  input_->SetDoubleRegister(i, 0.0);
102  }
103 
104  // Fill the frame content from the actual data on the frame.
105  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
106  input_->SetFrameSlot(i, Memory::uint64_at(tos + i));
107  }
108 }
109 
110 
111 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
112  // There is no dynamic alignment padding on ARM64 in the input frame.
113  return false;
114 }
115 
116 
117 void Deoptimizer::SetPlatformCompiledStubRegisters(
118  FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
119  ApiFunction function(descriptor->deoptimization_handler_);
120  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
121  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
122  int params = descriptor->GetHandlerParameterCount();
123  output_frame->SetRegister(x0.code(), params);
124  output_frame->SetRegister(x1.code(), handler);
125 }
126 
127 
128 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
129  for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
130  double double_value = input_->GetDoubleRegister(i);
131  output_frame->SetDoubleRegister(i, double_value);
132  }
133 }
134 
135 
136 Code* Deoptimizer::NotifyStubFailureBuiltin() {
137  return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
138 }
139 
140 
141 #define __ masm()->
142 
143 void Deoptimizer::EntryGenerator::Generate() {
144  GeneratePrologue();
145 
146  // TODO(all): This code needs to be revisited. We probably only need to save
147  // caller-saved registers here. Callee-saved registers can be stored directly
148  // in the input frame.
149 
150  // Save all allocatable floating point registers.
151  CPURegList saved_fp_registers(CPURegister::kFPRegister, kDRegSizeInBits,
153  __ PushCPURegList(saved_fp_registers);
154 
155  // We save all the registers expcept jssp, sp and lr.
156  CPURegList saved_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 27);
157  saved_registers.Combine(fp);
158  __ PushCPURegList(saved_registers);
159 
160  const int kSavedRegistersAreaSize =
161  (saved_registers.Count() * kXRegSize) +
162  (saved_fp_registers.Count() * kDRegSize);
163 
164  // Floating point registers are saved on the stack above core registers.
165  const int kFPRegistersOffset = saved_registers.Count() * kXRegSize;
166 
167  // Get the bailout id from the stack.
168  Register bailout_id = x2;
169  __ Peek(bailout_id, kSavedRegistersAreaSize);
170 
171  Register code_object = x3;
172  Register fp_to_sp = x4;
173  // Get the address of the location in the code object. This is the return
174  // address for lazy deoptimization.
175  __ Mov(code_object, lr);
176  // Compute the fp-to-sp delta, and correct one word for bailout id.
177  __ Add(fp_to_sp, masm()->StackPointer(),
178  kSavedRegistersAreaSize + (1 * kPointerSize));
179  __ Sub(fp_to_sp, fp, fp_to_sp);
180 
181  // Allocate a new deoptimizer object.
183  __ Mov(x1, type());
184  // Following arguments are already loaded:
185  // - x2: bailout id
186  // - x3: code object address
187  // - x4: fp-to-sp delta
188  __ Mov(x5, ExternalReference::isolate_address(isolate()));
189 
190  {
191  // Call Deoptimizer::New().
192  AllowExternalCallThatCantCauseGC scope(masm());
193  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
194  }
195 
196  // Preserve "deoptimizer" object in register x0.
197  Register deoptimizer = x0;
198 
199  // Get the input frame descriptor pointer.
200  __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset()));
201 
202  // Copy core registers into the input frame.
203  CPURegList copy_to_input = saved_registers;
204  for (int i = 0; i < saved_registers.Count(); i++) {
205  // TODO(all): Look for opportunities to optimize this by using ldp/stp.
206  __ Peek(x2, i * kPointerSize);
207  CPURegister current_reg = copy_to_input.PopLowestIndex();
208  int offset = (current_reg.code() * kPointerSize) +
210  __ Str(x2, MemOperand(x1, offset));
211  }
212 
213  // Copy FP registers to the input frame.
214  for (int i = 0; i < saved_fp_registers.Count(); i++) {
215  // TODO(all): Look for opportunities to optimize this by using ldp/stp.
216  int dst_offset = FrameDescription::double_registers_offset() +
217  (i * kDoubleSize);
218  int src_offset = kFPRegistersOffset + (i * kDoubleSize);
219  __ Peek(x2, src_offset);
220  __ Str(x2, MemOperand(x1, dst_offset));
221  }
222 
223  // Remove the bailout id and the saved registers from the stack.
224  __ Drop(1 + (kSavedRegistersAreaSize / kXRegSize));
225 
226  // Compute a pointer to the unwinding limit in register x2; that is
227  // the first stack slot not part of the input frame.
228  Register unwind_limit = x2;
229  __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset()));
230  __ Add(unwind_limit, unwind_limit, __ StackPointer());
231 
232  // Unwind the stack down to - but not including - the unwinding
233  // limit and copy the contents of the activation frame to the input
234  // frame description.
236  Label pop_loop;
237  Label pop_loop_header;
238  __ B(&pop_loop_header);
239  __ Bind(&pop_loop);
240  __ Pop(x4);
241  __ Str(x4, MemOperand(x3, kPointerSize, PostIndex));
242  __ Bind(&pop_loop_header);
243  __ Cmp(unwind_limit, __ StackPointer());
244  __ B(ne, &pop_loop);
245 
246  // Compute the output frame in the deoptimizer.
247  __ Push(x0); // Preserve deoptimizer object across call.
248 
249  {
250  // Call Deoptimizer::ComputeOutputFrames().
251  AllowExternalCallThatCantCauseGC scope(masm());
252  __ CallCFunction(
253  ExternalReference::compute_output_frames_function(isolate()), 1);
254  }
255  __ Pop(x4); // 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;
261  __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset()));
262  __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2));
263  __ B(&outer_loop_header);
264 
265  __ Bind(&outer_push_loop);
266  Register current_frame = x2;
267  __ Ldr(current_frame, MemOperand(x0, 0));
268  __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset()));
269  __ B(&inner_loop_header);
270 
271  __ Bind(&inner_push_loop);
272  __ Sub(x3, x3, kPointerSize);
273  __ Add(x6, current_frame, x3);
275  __ Push(x7);
276  __ Bind(&inner_loop_header);
277  __ Cbnz(x3, &inner_push_loop);
278 
279  __ Add(x0, x0, kPointerSize);
280  __ Bind(&outer_loop_header);
281  __ Cmp(x0, x1);
282  __ B(lt, &outer_push_loop);
283 
284  __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset()));
285  ASSERT(!saved_fp_registers.IncludesAliasOf(crankshaft_fp_scratch) &&
286  !saved_fp_registers.IncludesAliasOf(fp_zero) &&
287  !saved_fp_registers.IncludesAliasOf(fp_scratch));
289  while (!saved_fp_registers.IsEmpty()) {
290  const CPURegister reg = saved_fp_registers.PopLowestIndex();
291  __ Ldr(reg, MemOperand(x1, src_offset));
292  src_offset += kDoubleSize;
293  }
294 
295  // Push state from the last output frame.
296  __ Ldr(x6, MemOperand(current_frame, FrameDescription::state_offset()));
297  __ Push(x6);
298 
299  // TODO(all): ARM copies a lot (if not all) of the last output frame onto the
300  // stack, then pops it all into registers. Here, we try to load it directly
301  // into the relevant registers. Is this correct? If so, we should improve the
302  // ARM code.
303 
304  // TODO(all): This code needs to be revisited, We probably don't need to
305  // restore all the registers as fullcodegen does not keep live values in
306  // registers (note that at least fp must be restored though).
307 
308  // Restore registers from the last output frame.
309  // Note that lr is not in the list of saved_registers and will be restored
310  // later. We can use it to hold the address of last output frame while
311  // reloading the other registers.
312  ASSERT(!saved_registers.IncludesAliasOf(lr));
313  Register last_output_frame = lr;
314  __ Mov(last_output_frame, current_frame);
315 
316  // We don't need to restore x7 as it will be clobbered later to hold the
317  // continuation address.
318  Register continuation = x7;
319  saved_registers.Remove(continuation);
320 
321  while (!saved_registers.IsEmpty()) {
322  // TODO(all): Look for opportunities to optimize this by using ldp.
323  CPURegister current_reg = saved_registers.PopLowestIndex();
324  int offset = (current_reg.code() * kPointerSize) +
326  __ Ldr(current_reg, MemOperand(last_output_frame, offset));
327  }
328 
329  __ Ldr(continuation, MemOperand(last_output_frame,
331  __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset()));
332  __ InitializeRootRegister();
333  __ Br(continuation);
334 }
335 
336 
337 // Size of an entry of the second level deopt table.
338 // This is the code size generated by GeneratePrologue for one entry.
339 const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize;
340 
341 
343  UseScratchRegisterScope temps(masm());
344  Register entry_id = temps.AcquireX();
345 
346  // Create a sequence of deoptimization entries.
347  // Note that registers are still live when jumping to an entry.
348  Label done;
349  {
350  InstructionAccurateScope scope(masm());
351 
352  // The number of entry will never exceed kMaxNumberOfEntries.
353  // As long as kMaxNumberOfEntries is a valid 16 bits immediate you can use
354  // a movz instruction to load the entry id.
355  ASSERT(is_uint16(Deoptimizer::kMaxNumberOfEntries));
356 
357  for (int i = 0; i < count(); i++) {
358  int start = masm()->pc_offset();
359  USE(start);
360  __ movz(entry_id, i);
361  __ b(&done);
362  ASSERT(masm()->pc_offset() - start == table_entry_size_);
363  }
364  }
365  __ Bind(&done);
366  __ Push(entry_id);
367 }
368 
369 
370 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
371  SetFrameSlot(offset, value);
372 }
373 
374 
375 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
376  SetFrameSlot(offset, value);
377 }
378 
379 
380 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
381  // No out-of-line constant pool support.
382  UNREACHABLE();
383 }
384 
385 
386 #undef __
387 
388 } } // 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
void SetCallerPc(unsigned offset, intptr_t value)
Code * builtin(Name name)
Definition: builtins.h:322
static const RegList kAllocatableFPRegisters
const unsigned kDRegSizeInBits
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:503
const unsigned kXRegSizeInBits
Builtins * builtins()
Definition: isolate.h:948
#define ASSERT(condition)
Definition: checks.h:329
const int kPointerSizeLog2
Definition: globals.h:281
static SharedFunctionInfo * cast(Object *obj)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:526
static int double_registers_offset()
Definition: deoptimizer.h:582
#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 unsigned kInstructionSize
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
void SetCallerConstantPool(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:485
static int output_count_offset()
Definition: deoptimizer.h:247
const unsigned kXRegSize
const Register lr
#define __
const unsigned kDRegSize
static uint64_t & uint64_at(Address addr)
Definition: v8memory.h:55
void USE(T)
Definition: globals.h:341
virtual void GeneratePrologue()
const Register fp
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:536
void SetCallerFp(unsigned offset, intptr_t value)
void EvictFromOptimizedCodeMap(Code *optimized_code, const char *reason)
Definition: objects.cc:9593
static DeoptimizationInputData * cast(Object *obj)