• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 #if V8_TARGET_ARCH_X64
6 
7 #include "src/codegen.h"
8 #include "src/deoptimizer.h"
9 #include "src/full-codegen/full-codegen.h"
10 #include "src/objects-inl.h"
11 #include "src/register-configuration.h"
12 #include "src/safepoint-table.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 
18 const int Deoptimizer::table_entry_size_ = 10;
19 
20 
patch_size()21 int Deoptimizer::patch_size() {
22   return Assembler::kCallSequenceLength;
23 }
24 
25 
EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code)26 void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
27   // Empty because there is no need for relocation information for the code
28   // patching in Deoptimizer::PatchCodeForDeoptimization below.
29 }
30 
31 
PatchCodeForDeoptimization(Isolate * isolate,Code * code)32 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
33   // Invalidate the relocation information, as it will become invalid by the
34   // code patching below, and is not needed any more.
35   code->InvalidateRelocation();
36 
37   if (FLAG_zap_code_space) {
38     // Fail hard and early if we enter this code object again.
39     byte* pointer = code->FindCodeAgeSequence();
40     if (pointer != NULL) {
41       pointer += kNoCodeAgeSequenceLength;
42     } else {
43       pointer = code->instruction_start();
44     }
45     CodePatcher patcher(isolate, pointer, 1);
46     patcher.masm()->int3();
47 
48     DeoptimizationInputData* data =
49         DeoptimizationInputData::cast(code->deoptimization_data());
50     int osr_offset = data->OsrPcOffset()->value();
51     if (osr_offset > 0) {
52       CodePatcher osr_patcher(isolate, code->instruction_start() + osr_offset,
53                               1);
54       osr_patcher.masm()->int3();
55     }
56   }
57 
58   // For each LLazyBailout instruction insert a absolute call to the
59   // corresponding deoptimization entry, or a short call to an absolute
60   // jump if space is short. The absolute jumps are put in a table just
61   // before the safepoint table (space was allocated there when the Code
62   // object was created, if necessary).
63 
64   Address instruction_start = code->instruction_start();
65 #ifdef DEBUG
66   Address prev_call_address = NULL;
67 #endif
68   DeoptimizationInputData* deopt_data =
69       DeoptimizationInputData::cast(code->deoptimization_data());
70   deopt_data->SetSharedFunctionInfo(Smi::kZero);
71   // For each LLazyBailout instruction insert a call to the corresponding
72   // deoptimization entry.
73   for (int i = 0; i < deopt_data->DeoptCount(); i++) {
74     if (deopt_data->Pc(i)->value() == -1) continue;
75     // Position where Call will be patched in.
76     Address call_address = instruction_start + deopt_data->Pc(i)->value();
77     // There is room enough to write a long call instruction because we pad
78     // LLazyBailout instructions with nops if necessary.
79     CodePatcher patcher(isolate, call_address, Assembler::kCallSequenceLength);
80     patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
81                          Assembler::RelocInfoNone());
82     DCHECK(prev_call_address == NULL ||
83            call_address >= prev_call_address + patch_size());
84     DCHECK(call_address + patch_size() <= code->instruction_end());
85 #ifdef DEBUG
86     prev_call_address = call_address;
87 #endif
88   }
89 }
90 
91 
SetPlatformCompiledStubRegisters(FrameDescription * output_frame,CodeStubDescriptor * descriptor)92 void Deoptimizer::SetPlatformCompiledStubRegisters(
93     FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
94   intptr_t handler =
95       reinterpret_cast<intptr_t>(descriptor->deoptimization_handler());
96   int params = descriptor->GetHandlerParameterCount();
97   output_frame->SetRegister(rax.code(), params);
98   output_frame->SetRegister(rbx.code(), handler);
99 }
100 
101 
CopyDoubleRegisters(FrameDescription * output_frame)102 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
103   for (int i = 0; i < XMMRegister::kMaxNumRegisters; ++i) {
104     Float64 double_value = input_->GetDoubleRegister(i);
105     output_frame->SetDoubleRegister(i, double_value);
106   }
107 }
108 
109 #define __ masm()->
110 
Generate()111 void Deoptimizer::TableEntryGenerator::Generate() {
112   GeneratePrologue();
113 
114   // Save all general purpose registers before messing with them.
115   const int kNumberOfRegisters = Register::kNumRegisters;
116 
117   const int kDoubleRegsSize = kDoubleSize * XMMRegister::kMaxNumRegisters;
118   __ subp(rsp, Immediate(kDoubleRegsSize));
119 
120   const RegisterConfiguration* config = RegisterConfiguration::Crankshaft();
121   for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
122     int code = config->GetAllocatableDoubleCode(i);
123     XMMRegister xmm_reg = XMMRegister::from_code(code);
124     int offset = code * kDoubleSize;
125     __ Movsd(Operand(rsp, offset), xmm_reg);
126   }
127 
128   // We push all registers onto the stack, even though we do not need
129   // to restore all later.
130   for (int i = 0; i < kNumberOfRegisters; i++) {
131     Register r = Register::from_code(i);
132     __ pushq(r);
133   }
134 
135   const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize +
136                                       kDoubleRegsSize;
137 
138   __ Store(ExternalReference(Isolate::kCEntryFPAddress, isolate()), rbp);
139 
140   // We use this to keep the value of the fifth argument temporarily.
141   // Unfortunately we can't store it directly in r8 (used for passing
142   // this on linux), since it is another parameter passing register on windows.
143   Register arg5 = r11;
144 
145   // Get the bailout id from the stack.
146   __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
147 
148   // Get the address of the location in the code object
149   // and compute the fp-to-sp delta in register arg5.
150   __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize));
151   __ leap(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize +
152                             kPCOnStackSize));
153 
154   __ subp(arg5, rbp);
155   __ negp(arg5);
156 
157   // Allocate a new deoptimizer object.
158   __ PrepareCallCFunction(6);
159   __ movp(rax, Immediate(0));
160   Label context_check;
161   __ movp(rdi, Operand(rbp, CommonFrameConstants::kContextOrFrameTypeOffset));
162   __ JumpIfSmi(rdi, &context_check);
163   __ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
164   __ bind(&context_check);
165   __ movp(arg_reg_1, rax);
166   __ Set(arg_reg_2, type());
167   // Args 3 and 4 are already in the right registers.
168 
169   // On windows put the arguments on the stack (PrepareCallCFunction
170   // has created space for this). On linux pass the arguments in r8 and r9.
171 #ifdef _WIN64
172   __ movq(Operand(rsp, 4 * kRegisterSize), arg5);
173   __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
174   __ movq(Operand(rsp, 5 * kRegisterSize), arg5);
175 #else
176   __ movp(r8, arg5);
177   __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
178 #endif
179 
180   { AllowExternalCallThatCantCauseGC scope(masm());
181     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
182   }
183   // Preserve deoptimizer object in register rax and get the input
184   // frame descriptor pointer.
185   __ movp(rbx, Operand(rax, Deoptimizer::input_offset()));
186 
187   // Fill in the input registers.
188   for (int i = kNumberOfRegisters -1; i >= 0; i--) {
189     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
190     __ PopQuad(Operand(rbx, offset));
191   }
192 
193   // Fill in the double input registers.
194   int double_regs_offset = FrameDescription::double_registers_offset();
195   for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) {
196     int dst_offset = i * kDoubleSize + double_regs_offset;
197     __ popq(Operand(rbx, dst_offset));
198   }
199 
200   // Remove the bailout id and return address from the stack.
201   __ addp(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize));
202 
203   // Compute a pointer to the unwinding limit in register rcx; that is
204   // the first stack slot not part of the input frame.
205   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
206   __ addp(rcx, rsp);
207 
208   // Unwind the stack down to - but not including - the unwinding
209   // limit and copy the contents of the activation frame to the input
210   // frame description.
211   __ leap(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
212   Label pop_loop_header;
213   __ jmp(&pop_loop_header);
214   Label pop_loop;
215   __ bind(&pop_loop);
216   __ Pop(Operand(rdx, 0));
217   __ addp(rdx, Immediate(sizeof(intptr_t)));
218   __ bind(&pop_loop_header);
219   __ cmpp(rcx, rsp);
220   __ j(not_equal, &pop_loop);
221 
222   // Compute the output frame in the deoptimizer.
223   __ pushq(rax);
224   __ PrepareCallCFunction(2);
225   __ movp(arg_reg_1, rax);
226   __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
227   {
228     AllowExternalCallThatCantCauseGC scope(masm());
229     __ CallCFunction(
230         ExternalReference::compute_output_frames_function(isolate()), 2);
231   }
232   __ popq(rax);
233 
234   __ movp(rsp, Operand(rax, Deoptimizer::caller_frame_top_offset()));
235 
236   // Replace the current (input) frame with the output frames.
237   Label outer_push_loop, inner_push_loop,
238       outer_loop_header, inner_loop_header;
239   // Outer loop state: rax = current FrameDescription**, rdx = one past the
240   // last FrameDescription**.
241   __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
242   __ movp(rax, Operand(rax, Deoptimizer::output_offset()));
243   __ leap(rdx, Operand(rax, rdx, times_pointer_size, 0));
244   __ jmp(&outer_loop_header);
245   __ bind(&outer_push_loop);
246   // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
247   __ movp(rbx, Operand(rax, 0));
248   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
249   __ jmp(&inner_loop_header);
250   __ bind(&inner_push_loop);
251   __ subp(rcx, Immediate(sizeof(intptr_t)));
252   __ Push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
253   __ bind(&inner_loop_header);
254   __ testp(rcx, rcx);
255   __ j(not_zero, &inner_push_loop);
256   __ addp(rax, Immediate(kPointerSize));
257   __ bind(&outer_loop_header);
258   __ cmpp(rax, rdx);
259   __ j(below, &outer_push_loop);
260 
261   for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
262     int code = config->GetAllocatableDoubleCode(i);
263     XMMRegister xmm_reg = XMMRegister::from_code(code);
264     int src_offset = code * kDoubleSize + double_regs_offset;
265     __ Movsd(xmm_reg, Operand(rbx, src_offset));
266   }
267 
268   // Push state, pc, and continuation from the last output frame.
269   __ Push(Operand(rbx, FrameDescription::state_offset()));
270   __ PushQuad(Operand(rbx, FrameDescription::pc_offset()));
271   __ PushQuad(Operand(rbx, FrameDescription::continuation_offset()));
272 
273   // Push the registers from the last output frame.
274   for (int i = 0; i < kNumberOfRegisters; i++) {
275     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
276     __ PushQuad(Operand(rbx, offset));
277   }
278 
279   // Restore the registers from the stack.
280   for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
281     Register r = Register::from_code(i);
282     // Do not restore rsp, simply pop the value into the next register
283     // and overwrite this afterwards.
284     if (r.is(rsp)) {
285       DCHECK(i > 0);
286       r = Register::from_code(i - 1);
287     }
288     __ popq(r);
289   }
290 
291   // Set up the roots register.
292   __ InitializeRootRegister();
293 
294   // Return to the continuation point.
295   __ ret(0);
296 }
297 
298 
GeneratePrologue()299 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
300   // Create a sequence of deoptimization entries.
301   Label done;
302   for (int i = 0; i < count(); i++) {
303     int start = masm()->pc_offset();
304     USE(start);
305     __ pushq_imm32(i);
306     __ jmp(&done);
307     DCHECK(masm()->pc_offset() - start == table_entry_size_);
308   }
309   __ bind(&done);
310 }
311 
312 
SetCallerPc(unsigned offset,intptr_t value)313 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
314   if (kPCOnStackSize == 2 * kPointerSize) {
315     // Zero out the high-32 bit of PC for x32 port.
316     SetFrameSlot(offset + kPointerSize, 0);
317   }
318   SetFrameSlot(offset, value);
319 }
320 
321 
SetCallerFp(unsigned offset,intptr_t value)322 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
323   if (kFPOnStackSize == 2 * kPointerSize) {
324     // Zero out the high-32 bit of FP for x32 port.
325     SetFrameSlot(offset + kPointerSize, 0);
326   }
327   SetFrameSlot(offset, value);
328 }
329 
330 
SetCallerConstantPool(unsigned offset,intptr_t value)331 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
332   // No embedded constant pool support.
333   UNREACHABLE();
334 }
335 
336 
337 #undef __
338 
339 
340 }  // namespace internal
341 }  // namespace v8
342 
343 #endif  // V8_TARGET_ARCH_X64
344