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