V8 Project
deoptimizer-arm64.cc
Go to the documentation of this file.
1 // Copyright 2013 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 
13 namespace v8 {
14 namespace internal {
15 
16 
18  // Size of the code used to patch lazy bailout points.
19  // Patching is done by Deoptimizer::DeoptimizeFunction.
20  return 4 * kInstructionSize;
21 }
22 
23 
24 
25 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
26  // Invalidate the relocation information, as it will become invalid by the
27  // code patching below, and is not needed any more.
28  code->InvalidateRelocation();
29 
30  // TODO(jkummerow): if (FLAG_zap_code_space), make the code object's
31  // entry sequence unusable (see other architectures).
32 
33  DeoptimizationInputData* deopt_data =
34  DeoptimizationInputData::cast(code->deoptimization_data());
35  Address code_start_address = code->instruction_start();
36 #ifdef DEBUG
37  Address prev_call_address = NULL;
38 #endif
39  // For each LLazyBailout instruction insert a call to the corresponding
40  // deoptimization entry.
41  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
42  if (deopt_data->Pc(i)->value() == -1) continue;
43 
44  Address call_address = code_start_address + deopt_data->Pc(i)->value();
45  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
46 
47  PatchingAssembler patcher(call_address, patch_size() / kInstructionSize);
48  patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
49  patcher.blr(ip0);
50  patcher.dc64(reinterpret_cast<intptr_t>(deopt_entry));
51 
52  DCHECK((prev_call_address == NULL) ||
53  (call_address >= prev_call_address + patch_size()));
54  DCHECK(call_address + patch_size() <= code->instruction_end());
55 #ifdef DEBUG
56  prev_call_address = call_address;
57 #endif
58  }
59 }
60 
61 
62 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
63  // Set the register values. The values are not important as there are no
64  // callee saved registers in JavaScript frames, so all registers are
65  // spilled. Registers fp and sp are set to the correct values though.
66  for (int i = 0; i < Register::NumRegisters(); i++) {
67  input_->SetRegister(i, 0);
68  }
69 
70  // TODO(all): Do we also need to set a value to csp?
71  input_->SetRegister(jssp.code(), reinterpret_cast<intptr_t>(frame->sp()));
72  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
73 
74  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
75  input_->SetDoubleRegister(i, 0.0);
76  }
77 
78  // Fill the frame content from the actual data on the frame.
79  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
81  }
82 }
83 
84 
85 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
86  // There is no dynamic alignment padding on ARM64 in the input frame.
87  return false;
88 }
89 
90 
92  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
93  ApiFunction function(descriptor->deoptimization_handler());
94  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
95  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
96  int params = descriptor->GetHandlerParameterCount();
97  output_frame->SetRegister(x0.code(), params);
98  output_frame->SetRegister(x1.code(), handler);
99 }
100 
101 
102 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
103  for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
104  double double_value = input_->GetDoubleRegister(i);
105  output_frame->SetDoubleRegister(i, double_value);
106  }
107 }
108 
109 
110 
111 #define __ masm()->
112 
113 void Deoptimizer::EntryGenerator::Generate() {
114  GeneratePrologue();
115 
116  // TODO(all): This code needs to be revisited. We probably only need to save
117  // caller-saved registers here. Callee-saved registers can be stored directly
118  // in the input frame.
119 
120  // Save all allocatable floating point registers.
121  CPURegList saved_fp_registers(CPURegister::kFPRegister, kDRegSizeInBits,
123  __ PushCPURegList(saved_fp_registers);
124 
125  // We save all the registers expcept jssp, sp and lr.
126  CPURegList saved_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 27);
127  saved_registers.Combine(fp);
128  __ PushCPURegList(saved_registers);
129 
130  const int kSavedRegistersAreaSize =
131  (saved_registers.Count() * kXRegSize) +
132  (saved_fp_registers.Count() * kDRegSize);
133 
134  // Floating point registers are saved on the stack above core registers.
135  const int kFPRegistersOffset = saved_registers.Count() * kXRegSize;
136 
137  // Get the bailout id from the stack.
138  Register bailout_id = x2;
139  __ Peek(bailout_id, kSavedRegistersAreaSize);
140 
141  Register code_object = x3;
142  Register fp_to_sp = x4;
143  // Get the address of the location in the code object. This is the return
144  // address for lazy deoptimization.
145  __ Mov(code_object, lr);
146  // Compute the fp-to-sp delta, and correct one word for bailout id.
147  __ Add(fp_to_sp, masm()->StackPointer(),
148  kSavedRegistersAreaSize + (1 * kPointerSize));
149  __ Sub(fp_to_sp, fp, fp_to_sp);
150 
151  // Allocate a new deoptimizer object.
153  __ Mov(x1, type());
154  // Following arguments are already loaded:
155  // - x2: bailout id
156  // - x3: code object address
157  // - x4: fp-to-sp delta
158  __ Mov(x5, ExternalReference::isolate_address(isolate()));
159 
160  {
161  // Call Deoptimizer::New().
162  AllowExternalCallThatCantCauseGC scope(masm());
163  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
164  }
165 
166  // Preserve "deoptimizer" object in register x0.
167  Register deoptimizer = x0;
168 
169  // Get the input frame descriptor pointer.
170  __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset()));
171 
172  // Copy core registers into the input frame.
173  CPURegList copy_to_input = saved_registers;
174  for (int i = 0; i < saved_registers.Count(); i++) {
175  __ Peek(x2, i * kPointerSize);
176  CPURegister current_reg = copy_to_input.PopLowestIndex();
177  int offset = (current_reg.code() * kPointerSize) +
179  __ Str(x2, MemOperand(x1, offset));
180  }
181 
182  // Copy FP registers to the input frame.
183  for (int i = 0; i < saved_fp_registers.Count(); i++) {
184  int dst_offset = FrameDescription::double_registers_offset() +
185  (i * kDoubleSize);
186  int src_offset = kFPRegistersOffset + (i * kDoubleSize);
187  __ Peek(x2, src_offset);
188  __ Str(x2, MemOperand(x1, dst_offset));
189  }
190 
191  // Remove the bailout id and the saved registers from the stack.
192  __ Drop(1 + (kSavedRegistersAreaSize / kXRegSize));
193 
194  // Compute a pointer to the unwinding limit in register x2; that is
195  // the first stack slot not part of the input frame.
196  Register unwind_limit = x2;
197  __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset()));
198  __ Add(unwind_limit, unwind_limit, __ StackPointer());
199 
200  // Unwind the stack down to - but not including - the unwinding
201  // limit and copy the contents of the activation frame to the input
202  // frame description.
204  Label pop_loop;
205  Label pop_loop_header;
206  __ B(&pop_loop_header);
207  __ Bind(&pop_loop);
208  __ Pop(x4);
209  __ Str(x4, MemOperand(x3, kPointerSize, PostIndex));
210  __ Bind(&pop_loop_header);
211  __ Cmp(unwind_limit, __ StackPointer());
212  __ B(ne, &pop_loop);
213 
214  // Compute the output frame in the deoptimizer.
215  __ Push(x0); // Preserve deoptimizer object across call.
216 
217  {
218  // Call Deoptimizer::ComputeOutputFrames().
219  AllowExternalCallThatCantCauseGC scope(masm());
220  __ CallCFunction(
221  ExternalReference::compute_output_frames_function(isolate()), 1);
222  }
223  __ Pop(x4); // Restore deoptimizer object (class Deoptimizer).
224 
225  // Replace the current (input) frame with the output frames.
226  Label outer_push_loop, inner_push_loop,
227  outer_loop_header, inner_loop_header;
229  __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset()));
230  __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2));
231  __ B(&outer_loop_header);
232 
233  __ Bind(&outer_push_loop);
234  Register current_frame = x2;
235  __ Ldr(current_frame, MemOperand(x0, 0));
236  __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset()));
237  __ B(&inner_loop_header);
238 
239  __ Bind(&inner_push_loop);
240  __ Sub(x3, x3, kPointerSize);
241  __ Add(x6, current_frame, x3);
243  __ Push(x7);
244  __ Bind(&inner_loop_header);
245  __ Cbnz(x3, &inner_push_loop);
246 
247  __ Add(x0, x0, kPointerSize);
248  __ Bind(&outer_loop_header);
249  __ Cmp(x0, x1);
250  __ B(lt, &outer_push_loop);
251 
252  __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset()));
253  DCHECK(!saved_fp_registers.IncludesAliasOf(crankshaft_fp_scratch) &&
254  !saved_fp_registers.IncludesAliasOf(fp_zero) &&
255  !saved_fp_registers.IncludesAliasOf(fp_scratch));
257  while (!saved_fp_registers.IsEmpty()) {
258  const CPURegister reg = saved_fp_registers.PopLowestIndex();
259  __ Ldr(reg, MemOperand(x1, src_offset));
260  src_offset += kDoubleSize;
261  }
262 
263  // Push state from the last output frame.
264  __ Ldr(x6, MemOperand(current_frame, FrameDescription::state_offset()));
265  __ Push(x6);
266 
267  // TODO(all): ARM copies a lot (if not all) of the last output frame onto the
268  // stack, then pops it all into registers. Here, we try to load it directly
269  // into the relevant registers. Is this correct? If so, we should improve the
270  // ARM code.
271 
272  // TODO(all): This code needs to be revisited, We probably don't need to
273  // restore all the registers as fullcodegen does not keep live values in
274  // registers (note that at least fp must be restored though).
275 
276  // Restore registers from the last output frame.
277  // Note that lr is not in the list of saved_registers and will be restored
278  // later. We can use it to hold the address of last output frame while
279  // reloading the other registers.
280  DCHECK(!saved_registers.IncludesAliasOf(lr));
281  Register last_output_frame = lr;
282  __ Mov(last_output_frame, current_frame);
283 
284  // We don't need to restore x7 as it will be clobbered later to hold the
285  // continuation address.
286  Register continuation = x7;
287  saved_registers.Remove(continuation);
288 
289  while (!saved_registers.IsEmpty()) {
290  // TODO(all): Look for opportunities to optimize this by using ldp.
291  CPURegister current_reg = saved_registers.PopLowestIndex();
292  int offset = (current_reg.code() * kPointerSize) +
294  __ Ldr(current_reg, MemOperand(last_output_frame, offset));
295  }
296 
297  __ Ldr(continuation, MemOperand(last_output_frame,
299  __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset()));
300  __ InitializeRootRegister();
301  __ Br(continuation);
302 }
303 
304 
305 // Size of an entry of the second level deopt table.
306 // This is the code size generated by GeneratePrologue for one entry.
308 
309 
311  UseScratchRegisterScope temps(masm());
312  Register entry_id = temps.AcquireX();
313 
314  // Create a sequence of deoptimization entries.
315  // Note that registers are still live when jumping to an entry.
316  Label done;
317  {
318  InstructionAccurateScope scope(masm());
319 
320  // The number of entry will never exceed kMaxNumberOfEntries.
321  // As long as kMaxNumberOfEntries is a valid 16 bits immediate you can use
322  // a movz instruction to load the entry id.
324 
325  for (int i = 0; i < count(); i++) {
326  int start = masm()->pc_offset();
327  USE(start);
328  __ movz(entry_id, i);
329  __ b(&done);
330  DCHECK(masm()->pc_offset() - start == table_entry_size_);
331  }
332  }
333  __ Bind(&done);
334  __ Push(entry_id);
335 }
336 
337 
338 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
339  SetFrameSlot(offset, value);
340 }
341 
342 
343 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
344  SetFrameSlot(offset, value);
345 }
346 
347 
348 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
349  // No out-of-line constant pool support.
350  UNREACHABLE();
351 }
352 
353 
354 #undef __
355 
356 } } // namespace v8::internal
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
static const int kMaxNumberOfEntries
Definition: deoptimizer.h:296
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 uint64_t & uint64_at(Address addr)
Definition: v8memory.h:32
#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
void USE(T)
Definition: macros.h:322
static int Push(SpecialRPOStackFrame *stack, int depth, BasicBlock *child, int unvisited)
Definition: scheduler.cc:773
const int kPointerSize
Definition: globals.h:129
const unsigned kDRegSizeInBits
const unsigned kXRegSizeInBits
const int kDoubleSize
Definition: globals.h:127
const Register fp
const int kPointerSizeLog2
Definition: globals.h:147
const unsigned kLoadLiteralScaleLog2
const Register lr
byte * Address
Definition: globals.h:101
const unsigned kXRegSize
const unsigned kDRegSize
const unsigned kInstructionSize
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
static const int kMaxNumRegisters
static const RegList kAllocatableFPRegisters