Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: src/a64/deoptimizer-a64.cc

Issue 148293020: Merge experimental/a64 to bleeding_edge. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Remove ARM from OWNERS Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/a64/decoder-a64.cc ('k') | src/a64/disasm-a64.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 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 #include "codegen.h"
31 #include "deoptimizer.h"
32 #include "full-codegen.h"
33 #include "safepoint-table.h"
34
35
36 namespace v8 {
37 namespace internal {
38
39
40 int Deoptimizer::patch_size() {
41 // Size of the code used to patch lazy bailout points.
42 // Patching is done by Deoptimizer::DeoptimizeFunction.
43 return 4 * kInstructionSize;
44 }
45
46
47
48 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
49 // Invalidate the relocation information, as it will become invalid by the
50 // code patching below, and is not needed any more.
51 code->InvalidateRelocation();
52
53 // For each LLazyBailout instruction insert a call to the corresponding
54 // deoptimization entry.
55 DeoptimizationInputData* deopt_data =
56 DeoptimizationInputData::cast(code->deoptimization_data());
57 Address code_start_address = code->instruction_start();
58 #ifdef DEBUG
59 Address prev_call_address = NULL;
60 #endif
61
62 for (int i = 0; i < deopt_data->DeoptCount(); i++) {
63 if (deopt_data->Pc(i)->value() == -1) continue;
64
65 Address call_address = code_start_address + deopt_data->Pc(i)->value();
66 Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
67
68 PatchingAssembler patcher(call_address, patch_size() / kInstructionSize);
69 patcher.LoadLiteral(ip0, 2 * kInstructionSize);
70 patcher.blr(ip0);
71 patcher.dc64(reinterpret_cast<intptr_t>(deopt_entry));
72
73 ASSERT((prev_call_address == NULL) ||
74 (call_address >= prev_call_address + patch_size()));
75 ASSERT(call_address + patch_size() <= code->instruction_end());
76 #ifdef DEBUG
77 prev_call_address = call_address;
78 #endif
79 }
80 }
81
82
83 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
84 // Set the register values. The values are not important as there are no
85 // callee saved registers in JavaScript frames, so all registers are
86 // spilled. Registers fp and sp are set to the correct values though.
87 for (int i = 0; i < Register::NumRegisters(); i++) {
88 input_->SetRegister(i, 0);
89 }
90
91 // TODO(all): Do we also need to set a value to csp?
92 input_->SetRegister(jssp.code(), reinterpret_cast<intptr_t>(frame->sp()));
93 input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
94
95 for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
96 input_->SetDoubleRegister(i, 0.0);
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::uint64_at(tos + i));
102 }
103 }
104
105
106 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
107 // There is no dynamic alignment padding on A64 in the input frame.
108 return false;
109 }
110
111
112 void Deoptimizer::SetPlatformCompiledStubRegisters(
113 FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
114 ApiFunction function(descriptor->deoptimization_handler_);
115 ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
116 intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
117 int params = descriptor->GetHandlerParameterCount();
118 output_frame->SetRegister(x0.code(), params);
119 output_frame->SetRegister(x1.code(), handler);
120 }
121
122
123 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
124 for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
125 double double_value = input_->GetDoubleRegister(i);
126 output_frame->SetDoubleRegister(i, double_value);
127 }
128 }
129
130
131 Code* Deoptimizer::NotifyStubFailureBuiltin() {
132 return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
133 }
134
135
136 #define __ masm()->
137
138 void Deoptimizer::EntryGenerator::Generate() {
139 GeneratePrologue();
140
141 // TODO(all): This code needs to be revisited. We probably only need to save
142 // caller-saved registers here. Callee-saved registers can be stored directly
143 // in the input frame.
144
145 // Save all allocatable floating point registers.
146 CPURegList saved_fp_registers(CPURegister::kFPRegister, kDRegSize,
147 0, FPRegister::NumAllocatableRegisters() - 1);
148 __ PushCPURegList(saved_fp_registers);
149
150 // We save all the registers expcept jssp, sp and lr.
151 CPURegList saved_registers(CPURegister::kRegister, kXRegSize, 0, 27);
152 saved_registers.Combine(fp);
153 __ PushCPURegList(saved_registers);
154
155 const int kSavedRegistersAreaSize =
156 (saved_registers.Count() * kXRegSizeInBytes) +
157 (saved_fp_registers.Count() * kDRegSizeInBytes);
158
159 // Floating point registers are saved on the stack above core registers.
160 const int kFPRegistersOffset = saved_registers.Count() * kXRegSizeInBytes;
161
162 // Get the bailout id from the stack.
163 Register bailout_id = x2;
164 __ Peek(bailout_id, kSavedRegistersAreaSize);
165
166 Register code_object = x3;
167 Register fp_to_sp = x4;
168 // Get the address of the location in the code object. This is the return
169 // address for lazy deoptimization.
170 __ Mov(code_object, lr);
171 // Compute the fp-to-sp delta, and correct one word for bailout id.
172 __ Add(fp_to_sp, masm()->StackPointer(),
173 kSavedRegistersAreaSize + (1 * kPointerSize));
174 __ Sub(fp_to_sp, fp, fp_to_sp);
175
176 // Allocate a new deoptimizer object.
177 __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
178 __ Mov(x1, type());
179 // Following arguments are already loaded:
180 // - x2: bailout id
181 // - x3: code object address
182 // - x4: fp-to-sp delta
183 __ Mov(x5, Operand(ExternalReference::isolate_address(isolate())));
184
185 {
186 // Call Deoptimizer::New().
187 AllowExternalCallThatCantCauseGC scope(masm());
188 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
189 }
190
191 // Preserve "deoptimizer" object in register x0.
192 Register deoptimizer = x0;
193
194 // Get the input frame descriptor pointer.
195 __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset()));
196
197 // Copy core registers into the input frame.
198 CPURegList copy_to_input = saved_registers;
199 for (int i = 0; i < saved_registers.Count(); i++) {
200 // TODO(all): Look for opportunities to optimize this by using ldp/stp.
201 __ Peek(x2, i * kPointerSize);
202 CPURegister current_reg = copy_to_input.PopLowestIndex();
203 int offset = (current_reg.code() * kPointerSize) +
204 FrameDescription::registers_offset();
205 __ Str(x2, MemOperand(x1, offset));
206 }
207
208 // Copy FP registers to the input frame.
209 for (int i = 0; i < saved_fp_registers.Count(); i++) {
210 // TODO(all): Look for opportunities to optimize this by using ldp/stp.
211 int dst_offset = FrameDescription::double_registers_offset() +
212 (i * kDoubleSize);
213 int src_offset = kFPRegistersOffset + (i * kDoubleSize);
214 __ Peek(x2, src_offset);
215 __ Str(x2, MemOperand(x1, dst_offset));
216 }
217
218 // Remove the bailout id and the saved registers from the stack.
219 __ Drop(1 + (kSavedRegistersAreaSize / kXRegSizeInBytes));
220
221 // Compute a pointer to the unwinding limit in register x2; that is
222 // the first stack slot not part of the input frame.
223 Register unwind_limit = x2;
224 __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset()));
225 __ Add(unwind_limit, unwind_limit, __ StackPointer());
226
227 // Unwind the stack down to - but not including - the unwinding
228 // limit and copy the contents of the activation frame to the input
229 // frame description.
230 __ Add(x3, x1, FrameDescription::frame_content_offset());
231 Label pop_loop;
232 Label pop_loop_header;
233 __ B(&pop_loop_header);
234 __ Bind(&pop_loop);
235 __ Pop(x4);
236 __ Str(x4, MemOperand(x3, kPointerSize, PostIndex));
237 __ Bind(&pop_loop_header);
238 __ Cmp(unwind_limit, __ StackPointer());
239 __ B(ne, &pop_loop);
240
241 // Compute the output frame in the deoptimizer.
242 __ Push(x0); // Preserve deoptimizer object across call.
243
244 {
245 // Call Deoptimizer::ComputeOutputFrames().
246 AllowExternalCallThatCantCauseGC scope(masm());
247 __ CallCFunction(
248 ExternalReference::compute_output_frames_function(isolate()), 1);
249 }
250 __ Pop(x4); // Restore deoptimizer object (class Deoptimizer).
251
252 // Replace the current (input) frame with the output frames.
253 Label outer_push_loop, inner_push_loop,
254 outer_loop_header, inner_loop_header;
255 __ Ldrsw(x1, MemOperand(x4, Deoptimizer::output_count_offset()));
256 __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset()));
257 __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2));
258 __ B(&outer_loop_header);
259
260 __ Bind(&outer_push_loop);
261 Register current_frame = x2;
262 __ Ldr(current_frame, MemOperand(x0, 0));
263 __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset()));
264 __ B(&inner_loop_header);
265
266 __ Bind(&inner_push_loop);
267 __ Sub(x3, x3, kPointerSize);
268 __ Add(x6, current_frame, x3);
269 __ Ldr(x7, MemOperand(x6, FrameDescription::frame_content_offset()));
270 __ Push(x7);
271 __ Bind(&inner_loop_header);
272 __ Cbnz(x3, &inner_push_loop);
273
274 __ Add(x0, x0, kPointerSize);
275 __ Bind(&outer_loop_header);
276 __ Cmp(x0, x1);
277 __ B(lt, &outer_push_loop);
278
279 __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset()));
280 ASSERT(!saved_fp_registers.IncludesAliasOf(crankshaft_fp_scratch) &&
281 !saved_fp_registers.IncludesAliasOf(fp_zero) &&
282 !saved_fp_registers.IncludesAliasOf(fp_scratch));
283 int src_offset = FrameDescription::double_registers_offset();
284 while (!saved_fp_registers.IsEmpty()) {
285 const CPURegister reg = saved_fp_registers.PopLowestIndex();
286 __ Ldr(reg, MemOperand(x1, src_offset));
287 src_offset += kDoubleSize;
288 }
289
290 // Push state from the last output frame.
291 __ Ldr(x6, MemOperand(current_frame, FrameDescription::state_offset()));
292 __ Push(x6);
293
294 // TODO(all): ARM copies a lot (if not all) of the last output frame onto the
295 // stack, then pops it all into registers. Here, we try to load it directly
296 // into the relevant registers. Is this correct? If so, we should improve the
297 // ARM code.
298
299 // TODO(all): This code needs to be revisited, We probably don't need to
300 // restore all the registers as fullcodegen does not keep live values in
301 // registers (note that at least fp must be restored though).
302
303 // Restore registers from the last output frame.
304 // Note that lr is not in the list of saved_registers and will be restored
305 // later. We can use it to hold the address of last output frame while
306 // reloading the other registers.
307 ASSERT(!saved_registers.IncludesAliasOf(lr));
308 Register last_output_frame = lr;
309 __ Mov(last_output_frame, current_frame);
310
311 // We don't need to restore x7 as it will be clobbered later to hold the
312 // continuation address.
313 Register continuation = x7;
314 saved_registers.Remove(continuation);
315
316 while (!saved_registers.IsEmpty()) {
317 // TODO(all): Look for opportunities to optimize this by using ldp.
318 CPURegister current_reg = saved_registers.PopLowestIndex();
319 int offset = (current_reg.code() * kPointerSize) +
320 FrameDescription::registers_offset();
321 __ Ldr(current_reg, MemOperand(last_output_frame, offset));
322 }
323
324 __ Ldr(continuation, MemOperand(last_output_frame,
325 FrameDescription::continuation_offset()));
326 __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset()));
327 __ InitializeRootRegister();
328 __ Br(continuation);
329 }
330
331
332 // Size of an entry of the second level deopt table.
333 // This is the code size generated by GeneratePrologue for one entry.
334 const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize;
335
336
337 void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
338 // Create a sequence of deoptimization entries.
339 // Note that registers are still live when jumping to an entry.
340 Label done;
341 {
342 InstructionAccurateScope scope(masm());
343
344 // The number of entry will never exceed kMaxNumberOfEntries.
345 // As long as kMaxNumberOfEntries is a valid 16 bits immediate you can use
346 // a movz instruction to load the entry id.
347 ASSERT(is_uint16(Deoptimizer::kMaxNumberOfEntries));
348
349 for (int i = 0; i < count(); i++) {
350 int start = masm()->pc_offset();
351 USE(start);
352 __ movz(masm()->Tmp0(), i);
353 __ b(&done);
354 ASSERT(masm()->pc_offset() - start == table_entry_size_);
355 }
356 }
357 __ Bind(&done);
358 // TODO(all): We need to add some kind of assertion to verify that Tmp0()
359 // is not clobbered by Push.
360 __ Push(masm()->Tmp0());
361 }
362
363
364 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
365 SetFrameSlot(offset, value);
366 }
367
368
369 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
370 SetFrameSlot(offset, value);
371 }
372
373
374 #undef __
375
376 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/a64/decoder-a64.cc ('k') | src/a64/disasm-a64.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698