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