V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
deoptimizer-arm64.cc
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/api.h"
6 #include "src/arm64/assembler-arm64-inl.h"
7 #include "src/arm64/macro-assembler-arm64-inl.h"
8 #include "src/deoptimizer.h"
9 #include "src/frame-constants.h"
10 #include "src/register-configuration.h"
11 #include "src/safepoint-table.h"
12 
13 
14 namespace v8 {
15 namespace internal {
16 
17 #define __ masm()->
18 
19 namespace {
20 
21 void CopyRegListToFrame(MacroAssembler* masm, const Register& dst,
22  int dst_offset, const CPURegList& reg_list,
23  const Register& temp0, const Register& temp1,
24  int src_offset = 0) {
25  DCHECK_EQ(reg_list.Count() % 2, 0);
26  UseScratchRegisterScope temps(masm);
27  CPURegList copy_to_input = reg_list;
28  int reg_size = reg_list.RegisterSizeInBytes();
29  DCHECK_EQ(temp0.SizeInBytes(), reg_size);
30  DCHECK_EQ(temp1.SizeInBytes(), reg_size);
31 
32  // Compute some temporary addresses to avoid having the macro assembler set
33  // up a temp with an offset for accesses out of the range of the addressing
34  // mode.
35  Register src = temps.AcquireX();
36  masm->Add(src, sp, src_offset);
37  masm->Add(dst, dst, dst_offset);
38 
39  // Write reg_list into the frame pointed to by dst.
40  for (int i = 0; i < reg_list.Count(); i += 2) {
41  masm->Ldp(temp0, temp1, MemOperand(src, i * reg_size));
42 
43  CPURegister reg0 = copy_to_input.PopLowestIndex();
44  CPURegister reg1 = copy_to_input.PopLowestIndex();
45  int offset0 = reg0.code() * reg_size;
46  int offset1 = reg1.code() * reg_size;
47 
48  // Pair up adjacent stores, otherwise write them separately.
49  if (offset1 == offset0 + reg_size) {
50  masm->Stp(temp0, temp1, MemOperand(dst, offset0));
51  } else {
52  masm->Str(temp0, MemOperand(dst, offset0));
53  masm->Str(temp1, MemOperand(dst, offset1));
54  }
55  }
56  masm->Sub(dst, dst, dst_offset);
57 }
58 
59 void RestoreRegList(MacroAssembler* masm, const CPURegList& reg_list,
60  const Register& src_base, int src_offset) {
61  DCHECK_EQ(reg_list.Count() % 2, 0);
62  UseScratchRegisterScope temps(masm);
63  CPURegList restore_list = reg_list;
64  int reg_size = restore_list.RegisterSizeInBytes();
65 
66  // Compute a temporary addresses to avoid having the macro assembler set
67  // up a temp with an offset for accesses out of the range of the addressing
68  // mode.
69  Register src = temps.AcquireX();
70  masm->Add(src, src_base, src_offset);
71 
72 #if defined(V8_OS_WIN)
73  // x18 is reserved as platform register on Windows.
74  restore_list.Remove(x18);
75 #endif
76 
77  // Restore every register in restore_list from src.
78  while (!restore_list.IsEmpty()) {
79  CPURegister reg0 = restore_list.PopLowestIndex();
80  CPURegister reg1 = restore_list.PopLowestIndex();
81  int offset0 = reg0.code() * reg_size;
82 
83 #if defined(V8_OS_WIN)
84  if (reg1 == NoCPUReg) {
85  masm->Ldr(reg0, MemOperand(src, offset0));
86  break;
87  }
88 #endif
89 
90  int offset1 = reg1.code() * reg_size;
91 
92  // Pair up adjacent loads, otherwise read them separately.
93  if (offset1 == offset0 + reg_size) {
94  masm->Ldp(reg0, reg1, MemOperand(src, offset0));
95  } else {
96  masm->Ldr(reg0, MemOperand(src, offset0));
97  masm->Ldr(reg1, MemOperand(src, offset1));
98  }
99  }
100 }
101 } // namespace
102 
103 void Deoptimizer::TableEntryGenerator::Generate() {
104  GeneratePrologue();
105 
106  // TODO(all): This code needs to be revisited. We probably only need to save
107  // caller-saved registers here. Callee-saved registers can be stored directly
108  // in the input frame.
109 
110  // Save all allocatable double registers.
111  CPURegList saved_double_registers(
112  CPURegister::kVRegister, kDRegSizeInBits,
113  RegisterConfiguration::Default()->allocatable_double_codes_mask());
114  DCHECK_EQ(saved_double_registers.Count() % 2, 0);
115  __ PushCPURegList(saved_double_registers);
116 
117  CPURegList saved_float_registers(
118  CPURegister::kVRegister, kSRegSizeInBits,
119  RegisterConfiguration::Default()->allocatable_float_codes_mask());
120  DCHECK_EQ(saved_float_registers.Count() % 4, 0);
121  __ PushCPURegList(saved_float_registers);
122 
123  // We save all the registers except sp, lr and the masm scratches.
124  CPURegList saved_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 28);
125  saved_registers.Remove(ip0);
126  saved_registers.Remove(ip1);
127  saved_registers.Combine(fp);
128  DCHECK_EQ(saved_registers.Count() % 2, 0);
129  __ PushCPURegList(saved_registers);
130 
131  __ Mov(x3, Operand(ExternalReference::Create(
132  IsolateAddressId::kCEntryFPAddress, isolate())));
133  __ Str(fp, MemOperand(x3));
134 
135  const int kSavedRegistersAreaSize =
136  (saved_registers.Count() * kXRegSize) +
137  (saved_double_registers.Count() * kDRegSize) +
138  (saved_float_registers.Count() * kSRegSize);
139 
140  // Floating point registers are saved on the stack above core registers.
141  const int kFloatRegistersOffset = saved_registers.Count() * kXRegSize;
142  const int kDoubleRegistersOffset =
143  kFloatRegistersOffset + saved_float_registers.Count() * kSRegSize;
144 
145  // Get the bailout id from the stack.
146  Register bailout_id = x2;
147  __ Peek(bailout_id, kSavedRegistersAreaSize);
148 
149  Register code_object = x3;
150  Register fp_to_sp = x4;
151  // Get the address of the location in the code object. This is the return
152  // address for lazy deoptimization.
153  __ Mov(code_object, lr);
154  // Compute the fp-to-sp delta, adding two words for alignment padding and
155  // bailout id.
156  __ Add(fp_to_sp, sp, kSavedRegistersAreaSize + (2 * kPointerSize));
157  __ Sub(fp_to_sp, fp, fp_to_sp);
158 
159  // Allocate a new deoptimizer object.
160  __ Ldr(x1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
161 
162  // Ensure we can safely load from below fp.
163  DCHECK_GT(kSavedRegistersAreaSize,
164  -JavaScriptFrameConstants::kFunctionOffset);
165  __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
166 
167  // If x1 is a smi, zero x0.
168  __ Tst(x1, kSmiTagMask);
169  __ CzeroX(x0, eq);
170 
171  __ Mov(x1, static_cast<int>(deopt_kind()));
172  // Following arguments are already loaded:
173  // - x2: bailout id
174  // - x3: code object address
175  // - x4: fp-to-sp delta
176  __ Mov(x5, ExternalReference::isolate_address(isolate()));
177 
178  {
179  // Call Deoptimizer::New().
180  AllowExternalCallThatCantCauseGC scope(masm());
181  __ CallCFunction(ExternalReference::new_deoptimizer_function(), 6);
182  }
183 
184  // Preserve "deoptimizer" object in register x0.
185  Register deoptimizer = x0;
186 
187  // Get the input frame descriptor pointer.
188  __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset()));
189 
190  // Copy core registers into the input frame.
191  CopyRegListToFrame(masm(), x1, FrameDescription::registers_offset(),
192  saved_registers, x2, x3);
193 
194  // Copy double registers to the input frame.
195  CopyRegListToFrame(masm(), x1, FrameDescription::double_registers_offset(),
196  saved_double_registers, x2, x3, kDoubleRegistersOffset);
197 
198  // Copy float registers to the input frame.
199  // TODO(arm): these are the lower 32-bits of the double registers stored
200  // above, so we shouldn't need to store them again.
201  CopyRegListToFrame(masm(), x1, FrameDescription::float_registers_offset(),
202  saved_float_registers, w2, w3, kFloatRegistersOffset);
203 
204  // Remove the padding, bailout id and the saved registers from the stack.
205  DCHECK_EQ(kSavedRegistersAreaSize % kXRegSize, 0);
206  __ Drop(2 + (kSavedRegistersAreaSize / kXRegSize));
207 
208  // Compute a pointer to the unwinding limit in register x2; that is
209  // the first stack slot not part of the input frame.
210  Register unwind_limit = x2;
211  __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset()));
212 
213  // Unwind the stack down to - but not including - the unwinding
214  // limit and copy the contents of the activation frame to the input
215  // frame description.
216  __ Add(x3, x1, FrameDescription::frame_content_offset());
217  __ SlotAddress(x1, 0);
218  __ Lsr(unwind_limit, unwind_limit, kPointerSizeLog2);
219  __ Mov(x5, unwind_limit);
220  __ CopyDoubleWords(x3, x1, x5);
221  __ Drop(unwind_limit);
222 
223  // Compute the output frame in the deoptimizer.
224  __ Push(padreg, x0); // Preserve deoptimizer object across call.
225  {
226  // Call Deoptimizer::ComputeOutputFrames().
227  AllowExternalCallThatCantCauseGC scope(masm());
228  __ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
229  }
230  __ Pop(x4, padreg); // Restore deoptimizer object (class Deoptimizer).
231 
232  {
233  UseScratchRegisterScope temps(masm());
234  Register scratch = temps.AcquireX();
235  __ Ldr(scratch, MemOperand(x4, Deoptimizer::caller_frame_top_offset()));
236  __ Mov(sp, scratch);
237  }
238 
239  // Replace the current (input) frame with the output frames.
240  Label outer_push_loop, inner_push_loop,
241  outer_loop_header, inner_loop_header;
242  __ Ldrsw(x1, MemOperand(x4, Deoptimizer::output_count_offset()));
243  __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset()));
244  __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2));
245  __ B(&outer_loop_header);
246 
247  __ Bind(&outer_push_loop);
248  Register current_frame = x2;
249  Register frame_size = x3;
250  __ Ldr(current_frame, MemOperand(x0, kPointerSize, PostIndex));
251  __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset()));
252  __ Lsr(frame_size, x3, kPointerSizeLog2);
253  __ Claim(frame_size);
254 
255  __ Add(x7, current_frame, FrameDescription::frame_content_offset());
256  __ SlotAddress(x6, 0);
257  __ CopyDoubleWords(x6, x7, frame_size);
258 
259  __ Bind(&outer_loop_header);
260  __ Cmp(x0, x1);
261  __ B(lt, &outer_push_loop);
262 
263  __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset()));
264  RestoreRegList(masm(), saved_double_registers, x1,
265  FrameDescription::double_registers_offset());
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  // Restore registers from the last output frame.
273  // Note that lr is not in the list of saved_registers and will be restored
274  // later. We can use it to hold the address of last output frame while
275  // reloading the other registers.
276  DCHECK(!saved_registers.IncludesAliasOf(lr));
277  Register last_output_frame = lr;
278  __ Mov(last_output_frame, current_frame);
279 
280  RestoreRegList(masm(), saved_registers, last_output_frame,
281  FrameDescription::registers_offset());
282 
283  Register continuation = x7;
284  __ Ldr(continuation, MemOperand(last_output_frame,
285  FrameDescription::continuation_offset()));
286  __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset()));
287  __ Br(continuation);
288 }
289 
290 // Size of an entry of the second level deopt table. Since we do not generate
291 // a table for ARM64, the size is zero.
292 const int Deoptimizer::table_entry_size_ = 0 * kInstrSize;
293 
294 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
295  UseScratchRegisterScope temps(masm());
296  // The MacroAssembler will have put the deoptimization id in x16, the first
297  // temp register allocated. We can't assert that the id is in there, but we
298  // can check that x16 the first allocated temp and that the value it contains
299  // is in the expected range.
300  Register entry_id = temps.AcquireX();
301  DCHECK(entry_id.Is(x16));
302  __ Push(padreg, entry_id);
303 
304  if (__ emit_debug_code()) {
305  // Ensure the entry_id looks sensible, ie. 0 <= entry_id < count().
306  __ Cmp(entry_id, count());
307  __ Check(lo, AbortReason::kOffsetOutOfRange);
308  }
309 }
310 
311 bool Deoptimizer::PadTopOfStackRegister() { return true; }
312 
313 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
314  SetFrameSlot(offset, value);
315 }
316 
317 
318 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
319  SetFrameSlot(offset, value);
320 }
321 
322 
323 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
324  // No embedded constant pool support.
325  UNREACHABLE();
326 }
327 
328 
329 #undef __
330 
331 } // namespace internal
332 } // namespace v8
Definition: libplatform.h:13