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 
patch_size()17 int Deoptimizer::patch_size() {
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 
PatchCodeForDeoptimization(Isolate * isolate,Code * code)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 
FillInputFrame(Address tos,JavaScriptFrame * frame)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) {
80     input_->SetFrameSlot(i, Memory::uint64_at(tos + i));
81   }
82 }
83 
84 
HasAlignmentPadding(JSFunction * function)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 
SetPlatformCompiledStubRegisters(FrameDescription * output_frame,CodeStubDescriptor * descriptor)91 void Deoptimizer::SetPlatformCompiledStubRegisters(
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 
CopyDoubleRegisters(FrameDescription * output_frame)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 
Generate()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,
122                                 FPRegister::kAllocatableFPRegisters);
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.
152   __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
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) +
178         FrameDescription::registers_offset();
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.
203   __ Add(x3, x1, FrameDescription::frame_content_offset());
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;
228   __ Ldrsw(x1, MemOperand(x4, Deoptimizer::output_count_offset()));
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);
242   __ Ldr(x7, MemOperand(x6, FrameDescription::frame_content_offset()));
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));
256   int src_offset = FrameDescription::double_registers_offset();
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) +
293         FrameDescription::registers_offset();
294     __ Ldr(current_reg, MemOperand(last_output_frame, offset));
295   }
296 
297   __ Ldr(continuation, MemOperand(last_output_frame,
298                                   FrameDescription::continuation_offset()));
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.
307 const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize;
308 
309 
GeneratePrologue()310 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
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.
323     DCHECK(is_uint16(Deoptimizer::kMaxNumberOfEntries));
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 
SetCallerPc(unsigned offset,intptr_t value)338 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
339   SetFrameSlot(offset, value);
340 }
341 
342 
SetCallerFp(unsigned offset,intptr_t value)343 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
344   SetFrameSlot(offset, value);
345 }
346 
347 
SetCallerConstantPool(unsigned offset,intptr_t value)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
357