Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / v8 / src / x64 / deoptimizer-x64.cc
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "v8.h"
29
30 #if V8_TARGET_ARCH_X64
31
32 #include "codegen.h"
33 #include "deoptimizer.h"
34 #include "full-codegen.h"
35 #include "safepoint-table.h"
36
37 namespace v8 {
38 namespace internal {
39
40
41 const int Deoptimizer::table_entry_size_ = 10;
42
43
44 int Deoptimizer::patch_size() {
45   return Assembler::kCallSequenceLength;
46 }
47
48
49 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
50   // Invalidate the relocation information, as it will become invalid by the
51   // code patching below, and is not needed any more.
52   code->InvalidateRelocation();
53
54   // For each LLazyBailout instruction insert a absolute call to the
55   // corresponding deoptimization entry, or a short call to an absolute
56   // jump if space is short. The absolute jumps are put in a table just
57   // before the safepoint table (space was allocated there when the Code
58   // object was created, if necessary).
59
60   Address instruction_start = code->instruction_start();
61 #ifdef DEBUG
62   Address prev_call_address = NULL;
63 #endif
64   DeoptimizationInputData* deopt_data =
65       DeoptimizationInputData::cast(code->deoptimization_data());
66   for (int i = 0; i < deopt_data->DeoptCount(); i++) {
67     if (deopt_data->Pc(i)->value() == -1) continue;
68     // Position where Call will be patched in.
69     Address call_address = instruction_start + deopt_data->Pc(i)->value();
70     // There is room enough to write a long call instruction because we pad
71     // LLazyBailout instructions with nops if necessary.
72     CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
73     patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
74                          Assembler::RelocInfoNone());
75     ASSERT(prev_call_address == NULL ||
76            call_address >= prev_call_address + patch_size());
77     ASSERT(call_address + patch_size() <= code->instruction_end());
78 #ifdef DEBUG
79     prev_call_address = call_address;
80 #endif
81   }
82 }
83
84
85 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
86   // Set the register values. The values are not important as there are no
87   // callee saved registers in JavaScript frames, so all registers are
88   // spilled. Registers rbp and rsp are set to the correct values though.
89   for (int i = 0; i < Register::kNumRegisters; i++) {
90     input_->SetRegister(i, i * 4);
91   }
92   input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
93   input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
94   simd128_value_t zero = {{0.0, 0.0}};
95   for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
96     input_->SetSIMD128Register(i, zero);
97   }
98
99   // Fill the frame content from the actual data on the frame.
100   for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
101     input_->SetFrameSlot(i, Memory::uintptr_at(tos + i));
102   }
103 }
104
105
106 void Deoptimizer::SetPlatformCompiledStubRegisters(
107     FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
108   intptr_t handler =
109       reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
110   int params = descriptor->GetHandlerParameterCount();
111   output_frame->SetRegister(rax.code(), params);
112   output_frame->SetRegister(rbx.code(), handler);
113 }
114
115
116 void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) {
117   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
118     simd128_value_t xmm_value = input_->GetSIMD128Register(i);
119     output_frame->SetSIMD128Register(i, xmm_value);
120   }
121 }
122
123
124 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
125   // There is no dynamic alignment padding on x64 in the input frame.
126   return false;
127 }
128
129
130 Code* Deoptimizer::NotifyStubFailureBuiltin() {
131   return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
132 }
133
134
135 #define __ masm()->
136
137 void Deoptimizer::EntryGenerator::Generate() {
138   GeneratePrologue();
139
140   // Save all general purpose registers before messing with them.
141   const int kNumberOfRegisters = Register::kNumRegisters;
142
143   const int kXMMRegsSize = kSIMD128Size *
144       XMMRegister::NumAllocatableRegisters();
145   __ subq(rsp, Immediate(kXMMRegsSize));
146
147   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
148     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
149     int offset = i * kSIMD128Size;
150     __ movups(Operand(rsp, offset), xmm_reg);
151   }
152
153   // We push all registers onto the stack, even though we do not need
154   // to restore all later.
155   for (int i = 0; i < kNumberOfRegisters; i++) {
156     Register r = Register::from_code(i);
157     __ push(r);
158   }
159
160   const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize +
161                                       kXMMRegsSize;
162
163   // We use this to keep the value of the fifth argument temporarily.
164   // Unfortunately we can't store it directly in r8 (used for passing
165   // this on linux), since it is another parameter passing register on windows.
166   Register arg5 = r11;
167
168   // Get the bailout id from the stack.
169   __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
170
171   // Get the address of the location in the code object
172   // and compute the fp-to-sp delta in register arg5.
173   __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize));
174   __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize +
175                             kPCOnStackSize));
176
177   __ subq(arg5, rbp);
178   __ neg(arg5);
179
180   // Allocate a new deoptimizer object.
181   __ PrepareCallCFunction(6);
182   __ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
183   __ movp(arg_reg_1, rax);
184   __ Set(arg_reg_2, type());
185   // Args 3 and 4 are already in the right registers.
186
187   // On windows put the arguments on the stack (PrepareCallCFunction
188   // has created space for this). On linux pass the arguments in r8 and r9.
189 #ifdef _WIN64
190   __ movq(Operand(rsp, 4 * kRegisterSize), arg5);
191   __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
192   __ movq(Operand(rsp, 5 * kRegisterSize), arg5);
193 #else
194   __ movp(r8, arg5);
195   __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
196 #endif
197
198   { AllowExternalCallThatCantCauseGC scope(masm());
199     __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
200   }
201   // Preserve deoptimizer object in register rax and get the input
202   // frame descriptor pointer.
203   __ movp(rbx, Operand(rax, Deoptimizer::input_offset()));
204
205   // Fill in the input registers.
206   for (int i = kNumberOfRegisters -1; i >= 0; i--) {
207     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
208     __ pop(Operand(rbx, offset));
209   }
210
211   // Fill in the xmm input registers.
212   STATIC_ASSERT(kSIMD128Size == 2 * kDoubleSize);
213   int xmm_regs_offset = FrameDescription::simd128_registers_offset();
214   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
215     int dst_offset = i * kSIMD128Size + xmm_regs_offset;
216     __ pop(Operand(rbx, dst_offset));
217     __ pop(Operand(rbx, dst_offset + kDoubleSize));
218   }
219
220   // Remove the bailout id and return address from the stack.
221   __ addq(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize));
222
223   // Compute a pointer to the unwinding limit in register rcx; that is
224   // the first stack slot not part of the input frame.
225   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
226   __ addq(rcx, rsp);
227
228   // Unwind the stack down to - but not including - the unwinding
229   // limit and copy the contents of the activation frame to the input
230   // frame description.
231   __ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
232   Label pop_loop_header;
233   __ jmp(&pop_loop_header);
234   Label pop_loop;
235   __ bind(&pop_loop);
236   __ pop(Operand(rdx, 0));
237   __ addq(rdx, Immediate(sizeof(intptr_t)));
238   __ bind(&pop_loop_header);
239   __ cmpq(rcx, rsp);
240   __ j(not_equal, &pop_loop);
241
242   // Compute the output frame in the deoptimizer.
243   __ push(rax);
244   __ PrepareCallCFunction(2);
245   __ movp(arg_reg_1, rax);
246   __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
247   {
248     AllowExternalCallThatCantCauseGC scope(masm());
249     __ CallCFunction(
250         ExternalReference::compute_output_frames_function(isolate()), 2);
251   }
252   __ pop(rax);
253
254   // Replace the current frame with the output frames.
255   Label outer_push_loop, inner_push_loop,
256       outer_loop_header, inner_loop_header;
257   // Outer loop state: rax = current FrameDescription**, rdx = one past the
258   // last FrameDescription**.
259   __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
260   __ movp(rax, Operand(rax, Deoptimizer::output_offset()));
261   __ lea(rdx, Operand(rax, rdx, times_pointer_size, 0));
262   __ jmp(&outer_loop_header);
263   __ bind(&outer_push_loop);
264   // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
265   __ movp(rbx, Operand(rax, 0));
266   __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
267   __ jmp(&inner_loop_header);
268   __ bind(&inner_push_loop);
269   __ subq(rcx, Immediate(sizeof(intptr_t)));
270   __ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
271   __ bind(&inner_loop_header);
272   __ testq(rcx, rcx);
273   __ j(not_zero, &inner_push_loop);
274   __ addq(rax, Immediate(kPointerSize));
275   __ bind(&outer_loop_header);
276   __ cmpq(rax, rdx);
277   __ j(below, &outer_push_loop);
278
279   for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
280     XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
281     int src_offset = i * kSIMD128Size + xmm_regs_offset;
282     __ movups(xmm_reg, Operand(rbx, src_offset));
283   }
284
285   // Push state, pc, and continuation from the last output frame.
286   __ push(Operand(rbx, FrameDescription::state_offset()));
287   __ push(Operand(rbx, FrameDescription::pc_offset()));
288   __ push(Operand(rbx, FrameDescription::continuation_offset()));
289
290   // Push the registers from the last output frame.
291   for (int i = 0; i < kNumberOfRegisters; i++) {
292     int offset = (i * kPointerSize) + FrameDescription::registers_offset();
293     __ push(Operand(rbx, offset));
294   }
295
296   // Restore the registers from the stack.
297   for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
298     Register r = Register::from_code(i);
299     // Do not restore rsp, simply pop the value into the next register
300     // and overwrite this afterwards.
301     if (r.is(rsp)) {
302       ASSERT(i > 0);
303       r = Register::from_code(i - 1);
304     }
305     __ pop(r);
306   }
307
308   // Set up the roots register.
309   __ InitializeRootRegister();
310   __ InitializeSmiConstantRegister();
311
312   // Return to the continuation point.
313   __ ret(0);
314 }
315
316
317 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
318   // Create a sequence of deoptimization entries.
319   Label done;
320   for (int i = 0; i < count(); i++) {
321     int start = masm()->pc_offset();
322     USE(start);
323     __ push_imm32(i);
324     __ jmp(&done);
325     ASSERT(masm()->pc_offset() - start == table_entry_size_);
326   }
327   __ bind(&done);
328 }
329
330
331 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
332   SetFrameSlot(offset, value);
333 }
334
335
336 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
337   SetFrameSlot(offset, value);
338 }
339
340
341 double FrameDescription::GetDoubleRegister(unsigned n) const {
342   ASSERT(n < ARRAY_SIZE(simd128_registers_));
343   return simd128_registers_[n].d[0];
344 }
345
346
347 void FrameDescription::SetDoubleRegister(unsigned n, double value) {
348   ASSERT(n < ARRAY_SIZE(simd128_registers_));
349   simd128_registers_[n].d[0] = value;
350 }
351
352
353 #undef __
354
355
356 } }  // namespace v8::internal
357
358 #endif  // V8_TARGET_ARCH_X64