blob: 2080c615cad498ece0eaf3134d857c6e43afac2e [file] [log] [blame]
Steve Block1e0659c2011-05-24 12:43:12 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Ben Murdoch8c569c42011-03-10 11:43:29 +00002// 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
Ben Murdochb0fe1622011-05-05 13:52:32 +010028#include "v8.h"
Ben Murdoch8c569c42011-03-10 11:43:29 +000029
Ben Murdochb8e0da22011-05-16 14:20:40 +010030#if defined(V8_TARGET_ARCH_X64)
31
Ben Murdochb0fe1622011-05-05 13:52:32 +010032#include "codegen.h"
33#include "deoptimizer.h"
34#include "full-codegen.h"
35#include "safepoint-table.h"
Ben Murdoch8c569c42011-03-10 11:43:29 +000036
Ben Murdochb0fe1622011-05-05 13:52:32 +010037namespace v8 {
38namespace internal {
Ben Murdoch8c569c42011-03-10 11:43:29 +000039
Ben Murdochb0fe1622011-05-05 13:52:32 +010040
41int Deoptimizer::table_entry_size_ = 10;
42
Steve Block1e0659c2011-05-24 12:43:12 +010043
44int Deoptimizer::patch_size() {
45 return MacroAssembler::kCallInstructionLength;
Ben Murdochb0fe1622011-05-05 13:52:32 +010046}
47
48
Steve Block1e0659c2011-05-24 12:43:12 +010049#ifdef DEBUG
50// Overwrites code with int3 instructions.
51static void ZapCodeRange(Address from, Address to) {
52 CHECK(from <= to);
53 int length = static_cast<int>(to - from);
54 CodePatcher destroyer(from, length);
55 while (length-- > 0) {
56 destroyer.masm()->int3();
57 }
58}
59#endif
60
61
62// Iterate through the entries of a SafepointTable that corresponds to
63// deoptimization points.
64class SafepointTableDeoptimiztionEntryIterator {
65 public:
66 explicit SafepointTableDeoptimiztionEntryIterator(Code* code)
67 : code_(code), table_(code), index_(-1), limit_(table_.length()) {
68 FindNextIndex();
69 }
70
71 SafepointEntry Next(Address* pc) {
72 if (index_ >= limit_) {
73 *pc = NULL;
74 return SafepointEntry(); // Invalid entry.
75 }
76 *pc = code_->instruction_start() + table_.GetPcOffset(index_);
77 SafepointEntry entry = table_.GetEntry(index_);
78 FindNextIndex();
79 return entry;
80 }
81
82 private:
83 void FindNextIndex() {
84 ASSERT(index_ < limit_);
85 while (++index_ < limit_) {
86 if (table_.GetEntry(index_).deoptimization_index() !=
87 Safepoint::kNoDeoptimizationIndex) {
88 return;
89 }
90 }
91 }
92
93 Code* code_;
94 SafepointTable table_;
95 // Index of next deoptimization entry. If negative after calling
96 // FindNextIndex, there are no more, and Next will return an invalid
97 // SafepointEntry.
98 int index_;
99 // Table length.
100 int limit_;
101};
102
103
Steve Block44f0eee2011-05-26 01:26:41 +0100104void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
105 // TODO(1276): Implement.
106}
107
108
Steve Block1e0659c2011-05-24 12:43:12 +0100109void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
Steve Block44f0eee2011-05-26 01:26:41 +0100110 HandleScope scope;
Steve Block1e0659c2011-05-24 12:43:12 +0100111 AssertNoAllocation no_allocation;
112
113 if (!function->IsOptimized()) return;
114
115 // Get the optimized code.
116 Code* code = function->code();
117
118 // Invalidate the relocation information, as it will become invalid by the
119 // code patching below, and is not needed any more.
120 code->InvalidateRelocation();
121
122 // For each return after a safepoint insert a absolute call to the
123 // corresponding deoptimization entry, or a short call to an absolute
124 // jump if space is short. The absolute jumps are put in a table just
125 // before the safepoint table (space was allocated there when the Code
126 // object was created, if necessary).
127
128 Address instruction_start = function->code()->instruction_start();
129 Address jump_table_address =
130 instruction_start + function->code()->safepoint_table_offset();
131 Address previous_pc = instruction_start;
132
133 SafepointTableDeoptimiztionEntryIterator deoptimizations(function->code());
134 Address entry_pc = NULL;
135
136 SafepointEntry current_entry = deoptimizations.Next(&entry_pc);
137 while (current_entry.is_valid()) {
138 int gap_code_size = current_entry.gap_code_size();
139 unsigned deoptimization_index = current_entry.deoptimization_index();
140
141#ifdef DEBUG
142 // Destroy the code which is not supposed to run again.
143 ZapCodeRange(previous_pc, entry_pc);
144#endif
145 // Position where Call will be patched in.
146 Address call_address = entry_pc + gap_code_size;
147 // End of call instruction, if using a direct call to a 64-bit address.
148 Address call_end_address =
149 call_address + MacroAssembler::kCallInstructionLength;
150
151 // Find next deoptimization entry, if any.
152 Address next_pc = NULL;
153 SafepointEntry next_entry = deoptimizations.Next(&next_pc);
154
155 if (!next_entry.is_valid() || next_pc >= call_end_address) {
156 // Room enough to write a long call instruction.
157 CodePatcher patcher(call_address, Assembler::kCallInstructionLength);
158 patcher.masm()->Call(GetDeoptimizationEntry(deoptimization_index, LAZY),
159 RelocInfo::NONE);
160 previous_pc = call_end_address;
161 } else {
162 // Not room enough for a long Call instruction. Write a short call
163 // instruction to a long jump placed elsewhere in the code.
164 Address short_call_end_address =
165 call_address + MacroAssembler::kShortCallInstructionLength;
166 ASSERT(next_pc >= short_call_end_address);
167
168 // Write jump in jump-table.
169 jump_table_address -= MacroAssembler::kJumpInstructionLength;
170 CodePatcher jump_patcher(jump_table_address,
171 MacroAssembler::kJumpInstructionLength);
172 jump_patcher.masm()->Jump(
173 GetDeoptimizationEntry(deoptimization_index, LAZY),
174 RelocInfo::NONE);
175
176 // Write call to jump at call_offset.
177 CodePatcher call_patcher(call_address,
178 MacroAssembler::kShortCallInstructionLength);
179 call_patcher.masm()->call(jump_table_address);
180 previous_pc = short_call_end_address;
181 }
182
183 // Continue with next deoptimization entry.
184 current_entry = next_entry;
185 entry_pc = next_pc;
186 }
187
188#ifdef DEBUG
189 // Destroy the code which is not supposed to run again.
190 ZapCodeRange(previous_pc, jump_table_address);
191#endif
192
193 // Add the deoptimizing code to the list.
194 DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code);
Steve Block44f0eee2011-05-26 01:26:41 +0100195 DeoptimizerData* data = code->GetIsolate()->deoptimizer_data();
196 node->set_next(data->deoptimizing_code_list_);
197 data->deoptimizing_code_list_ = node;
Steve Block1e0659c2011-05-24 12:43:12 +0100198
199 // Set the code for the function to non-optimized version.
200 function->ReplaceCode(function->shared()->code());
201
202 if (FLAG_trace_deopt) {
203 PrintF("[forced deoptimization: ");
204 function->PrintName();
205 PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
Steve Block44f0eee2011-05-26 01:26:41 +0100206#ifdef DEBUG
207 if (FLAG_print_code) {
208 code->PrintLn();
209 }
210#endif
Steve Block1e0659c2011-05-24 12:43:12 +0100211 }
212}
213
214
215void Deoptimizer::PatchStackCheckCodeAt(Address pc_after,
216 Code* check_code,
217 Code* replacement_code) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100218 Address call_target_address = pc_after - kIntSize;
219 ASSERT(check_code->entry() ==
220 Assembler::target_address_at(call_target_address));
221 // The stack check code matches the pattern:
222 //
223 // cmp rsp, <limit>
224 // jae ok
225 // call <stack guard>
226 // test rax, <loop nesting depth>
227 // ok: ...
228 //
229 // We will patch away the branch so the code is:
230 //
231 // cmp rsp, <limit> ;; Not changed
232 // nop
233 // nop
234 // call <on-stack replacment>
235 // test rax, <loop nesting depth>
236 // ok:
237 //
238 ASSERT(*(call_target_address - 3) == 0x73 && // jae
239 *(call_target_address - 2) == 0x07 && // offset
240 *(call_target_address - 1) == 0xe8); // call
241 *(call_target_address - 3) = 0x90; // nop
242 *(call_target_address - 2) = 0x90; // nop
243 Assembler::set_target_address_at(call_target_address,
244 replacement_code->entry());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100245}
246
247
Steve Block1e0659c2011-05-24 12:43:12 +0100248void Deoptimizer::RevertStackCheckCodeAt(Address pc_after,
249 Code* check_code,
250 Code* replacement_code) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100251 Address call_target_address = pc_after - kIntSize;
252 ASSERT(replacement_code->entry() ==
253 Assembler::target_address_at(call_target_address));
254 // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
255 // restore the conditional branch.
256 ASSERT(*(call_target_address - 3) == 0x90 && // nop
257 *(call_target_address - 2) == 0x90 && // nop
258 *(call_target_address - 1) == 0xe8); // call
259 *(call_target_address - 3) = 0x73; // jae
260 *(call_target_address - 2) = 0x07; // offset
261 Assembler::set_target_address_at(call_target_address,
262 check_code->entry());
263}
264
265
266static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) {
267 ByteArray* translations = data->TranslationByteArray();
268 int length = data->DeoptCount();
269 for (int i = 0; i < length; i++) {
270 if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) {
271 TranslationIterator it(translations, data->TranslationIndex(i)->value());
272 int value = it.Next();
273 ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
274 // Read the number of frames.
275 value = it.Next();
276 if (value == 1) return i;
277 }
278 }
279 UNREACHABLE();
280 return -1;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100281}
282
283
284void Deoptimizer::DoComputeOsrOutputFrame() {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100285 DeoptimizationInputData* data = DeoptimizationInputData::cast(
286 optimized_code_->deoptimization_data());
287 unsigned ast_id = data->OsrAstId()->value();
288 // TODO(kasperl): This should not be the bailout_id_. It should be
289 // the ast id. Confusing.
290 ASSERT(bailout_id_ == ast_id);
291
292 int bailout_id = LookupBailoutId(data, ast_id);
293 unsigned translation_index = data->TranslationIndex(bailout_id)->value();
294 ByteArray* translations = data->TranslationByteArray();
295
296 TranslationIterator iterator(translations, translation_index);
297 Translation::Opcode opcode =
298 static_cast<Translation::Opcode>(iterator.Next());
299 ASSERT(Translation::BEGIN == opcode);
300 USE(opcode);
301 int count = iterator.Next();
302 ASSERT(count == 1);
303 USE(count);
304
305 opcode = static_cast<Translation::Opcode>(iterator.Next());
306 USE(opcode);
307 ASSERT(Translation::FRAME == opcode);
308 unsigned node_id = iterator.Next();
309 USE(node_id);
310 ASSERT(node_id == ast_id);
311 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator.Next()));
312 USE(function);
313 ASSERT(function == function_);
314 unsigned height = iterator.Next();
315 unsigned height_in_bytes = height * kPointerSize;
316 USE(height_in_bytes);
317
318 unsigned fixed_size = ComputeFixedSize(function_);
319 unsigned input_frame_size = static_cast<unsigned>(input_->GetFrameSize());
320 ASSERT(fixed_size + height_in_bytes == input_frame_size);
321
322 unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize;
323 unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
324 unsigned outgoing_size = outgoing_height * kPointerSize;
325 unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
326 ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call.
327
328 if (FLAG_trace_osr) {
329 PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
330 reinterpret_cast<intptr_t>(function_));
331 function_->PrintName();
332 PrintF(" => node=%u, frame=%d->%d]\n",
333 ast_id,
334 input_frame_size,
335 output_frame_size);
336 }
337
338 // There's only one output frame in the OSR case.
339 output_count_ = 1;
340 output_ = new FrameDescription*[1];
341 output_[0] = new(output_frame_size) FrameDescription(
342 output_frame_size, function_);
343
344 // Clear the incoming parameters in the optimized frame to avoid
345 // confusing the garbage collector.
346 unsigned output_offset = output_frame_size - kPointerSize;
347 int parameter_count = function_->shared()->formal_parameter_count() + 1;
348 for (int i = 0; i < parameter_count; ++i) {
349 output_[0]->SetFrameSlot(output_offset, 0);
350 output_offset -= kPointerSize;
351 }
352
353 // Translate the incoming parameters. This may overwrite some of the
354 // incoming argument slots we've just cleared.
355 int input_offset = input_frame_size - kPointerSize;
356 bool ok = true;
357 int limit = input_offset - (parameter_count * kPointerSize);
358 while (ok && input_offset > limit) {
359 ok = DoOsrTranslateCommand(&iterator, &input_offset);
360 }
361
362 // There are no translation commands for the caller's pc and fp, the
363 // context, and the function. Set them up explicitly.
Steve Block44f0eee2011-05-26 01:26:41 +0100364 for (int i = StandardFrameConstants::kCallerPCOffset;
365 ok && i >= StandardFrameConstants::kMarkerOffset;
366 i -= kPointerSize) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100367 intptr_t input_value = input_->GetFrameSlot(input_offset);
368 if (FLAG_trace_osr) {
Steve Block44f0eee2011-05-26 01:26:41 +0100369 const char* name = "UNKNOWN";
370 switch (i) {
371 case StandardFrameConstants::kCallerPCOffset:
372 name = "caller's pc";
373 break;
374 case StandardFrameConstants::kCallerFPOffset:
375 name = "fp";
376 break;
377 case StandardFrameConstants::kContextOffset:
378 name = "context";
379 break;
380 case StandardFrameConstants::kMarkerOffset:
381 name = "function";
382 break;
383 }
384 PrintF(" [rsp + %d] <- 0x%08" V8PRIxPTR " ; [rsp + %d] "
385 "(fixed part - %s)\n",
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100386 output_offset,
387 input_value,
Steve Block44f0eee2011-05-26 01:26:41 +0100388 input_offset,
389 name);
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100390 }
391 output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
392 input_offset -= kPointerSize;
393 output_offset -= kPointerSize;
394 }
395
396 // Translate the rest of the frame.
397 while (ok && input_offset >= 0) {
398 ok = DoOsrTranslateCommand(&iterator, &input_offset);
399 }
400
401 // If translation of any command failed, continue using the input frame.
402 if (!ok) {
403 delete output_[0];
404 output_[0] = input_;
405 output_[0]->SetPc(reinterpret_cast<intptr_t>(from_));
406 } else {
407 // Setup the frame pointer and the context pointer.
408 output_[0]->SetRegister(rbp.code(), input_->GetRegister(rbp.code()));
409 output_[0]->SetRegister(rsi.code(), input_->GetRegister(rsi.code()));
410
411 unsigned pc_offset = data->OsrPcOffset()->value();
412 intptr_t pc = reinterpret_cast<intptr_t>(
413 optimized_code_->entry() + pc_offset);
414 output_[0]->SetPc(pc);
415 }
Steve Block44f0eee2011-05-26 01:26:41 +0100416 Code* continuation =
417 function->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100418 output_[0]->SetContinuation(
419 reinterpret_cast<intptr_t>(continuation->entry()));
420
421 if (FLAG_trace_osr) {
422 PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
423 ok ? "finished" : "aborted",
424 reinterpret_cast<intptr_t>(function));
425 function->PrintName();
426 PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc());
427 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100428}
429
430
431void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
432 int frame_index) {
Steve Block1e0659c2011-05-24 12:43:12 +0100433 // Read the ast node id, function, and frame height for this output frame.
434 Translation::Opcode opcode =
435 static_cast<Translation::Opcode>(iterator->Next());
436 USE(opcode);
437 ASSERT(Translation::FRAME == opcode);
438 int node_id = iterator->Next();
439 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
440 unsigned height = iterator->Next();
441 unsigned height_in_bytes = height * kPointerSize;
442 if (FLAG_trace_deopt) {
443 PrintF(" translating ");
444 function->PrintName();
445 PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes);
446 }
447
448 // The 'fixed' part of the frame consists of the incoming parameters and
449 // the part described by JavaScriptFrameConstants.
450 unsigned fixed_frame_size = ComputeFixedSize(function);
451 unsigned input_frame_size = static_cast<unsigned>(input_->GetFrameSize());
452 unsigned output_frame_size = height_in_bytes + fixed_frame_size;
453
454 // Allocate and store the output frame description.
455 FrameDescription* output_frame =
456 new(output_frame_size) FrameDescription(output_frame_size, function);
457
458 bool is_bottommost = (0 == frame_index);
459 bool is_topmost = (output_count_ - 1 == frame_index);
460 ASSERT(frame_index >= 0 && frame_index < output_count_);
461 ASSERT(output_[frame_index] == NULL);
462 output_[frame_index] = output_frame;
463
464 // The top address for the bottommost output frame can be computed from
465 // the input frame pointer and the output frame's height. For all
466 // subsequent output frames, it can be computed from the previous one's
467 // top address and the current frame's size.
468 intptr_t top_address;
469 if (is_bottommost) {
470 // 2 = context and function in the frame.
471 top_address =
472 input_->GetRegister(rbp.code()) - (2 * kPointerSize) - height_in_bytes;
473 } else {
474 top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
475 }
476 output_frame->SetTop(top_address);
477
478 // Compute the incoming parameter translation.
479 int parameter_count = function->shared()->formal_parameter_count() + 1;
480 unsigned output_offset = output_frame_size;
481 unsigned input_offset = input_frame_size;
482 for (int i = 0; i < parameter_count; ++i) {
483 output_offset -= kPointerSize;
484 DoTranslateCommand(iterator, frame_index, output_offset);
485 }
486 input_offset -= (parameter_count * kPointerSize);
487
488 // There are no translation commands for the caller's pc and fp, the
489 // context, and the function. Synthesize their values and set them up
490 // explicitly.
491 //
492 // The caller's pc for the bottommost output frame is the same as in the
493 // input frame. For all subsequent output frames, it can be read from the
494 // previous one. This frame's pc can be computed from the non-optimized
495 // function code and AST id of the bailout.
496 output_offset -= kPointerSize;
497 input_offset -= kPointerSize;
498 intptr_t value;
499 if (is_bottommost) {
500 value = input_->GetFrameSlot(input_offset);
501 } else {
502 value = output_[frame_index - 1]->GetPc();
503 }
504 output_frame->SetFrameSlot(output_offset, value);
505 if (FLAG_trace_deopt) {
506 PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
507 V8PRIxPTR " ; caller's pc\n",
508 top_address + output_offset, output_offset, value);
509 }
510
511 // The caller's frame pointer for the bottommost output frame is the same
512 // as in the input frame. For all subsequent output frames, it can be
513 // read from the previous one. Also compute and set this frame's frame
514 // pointer.
515 output_offset -= kPointerSize;
516 input_offset -= kPointerSize;
517 if (is_bottommost) {
518 value = input_->GetFrameSlot(input_offset);
519 } else {
520 value = output_[frame_index - 1]->GetFp();
521 }
522 output_frame->SetFrameSlot(output_offset, value);
523 intptr_t fp_value = top_address + output_offset;
524 ASSERT(!is_bottommost || input_->GetRegister(rbp.code()) == fp_value);
525 output_frame->SetFp(fp_value);
526 if (is_topmost) output_frame->SetRegister(rbp.code(), fp_value);
527 if (FLAG_trace_deopt) {
528 PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
529 V8PRIxPTR " ; caller's fp\n",
530 fp_value, output_offset, value);
531 }
532
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100533 // For the bottommost output frame the context can be gotten from the input
534 // frame. For all subsequent output frames it can be gotten from the function
535 // so long as we don't inline functions that need local contexts.
Steve Block1e0659c2011-05-24 12:43:12 +0100536 output_offset -= kPointerSize;
537 input_offset -= kPointerSize;
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100538 if (is_bottommost) {
539 value = input_->GetFrameSlot(input_offset);
540 } else {
541 value = reinterpret_cast<intptr_t>(function->context());
542 }
Steve Block1e0659c2011-05-24 12:43:12 +0100543 output_frame->SetFrameSlot(output_offset, value);
544 if (is_topmost) output_frame->SetRegister(rsi.code(), value);
545 if (FLAG_trace_deopt) {
546 PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
547 V8PRIxPTR "; context\n",
548 top_address + output_offset, output_offset, value);
549 }
550
551 // The function was mentioned explicitly in the BEGIN_FRAME.
552 output_offset -= kPointerSize;
553 input_offset -= kPointerSize;
554 value = reinterpret_cast<intptr_t>(function);
555 // The function for the bottommost output frame should also agree with the
556 // input frame.
557 ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
558 output_frame->SetFrameSlot(output_offset, value);
559 if (FLAG_trace_deopt) {
560 PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
561 V8PRIxPTR "; function\n",
562 top_address + output_offset, output_offset, value);
563 }
564
565 // Translate the rest of the frame.
566 for (unsigned i = 0; i < height; ++i) {
567 output_offset -= kPointerSize;
568 DoTranslateCommand(iterator, frame_index, output_offset);
569 }
570 ASSERT(0 == output_offset);
571
572 // Compute this frame's PC, state, and continuation.
573 Code* non_optimized_code = function->shared()->code();
574 FixedArray* raw_data = non_optimized_code->deoptimization_data();
575 DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
576 Address start = non_optimized_code->instruction_start();
577 unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
578 unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
579 intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
580 output_frame->SetPc(pc_value);
581
582 FullCodeGenerator::State state =
583 FullCodeGenerator::StateField::decode(pc_and_state);
584 output_frame->SetState(Smi::FromInt(state));
585
586 // Set the continuation for the topmost frame.
587 if (is_topmost) {
588 Code* continuation = (bailout_type_ == EAGER)
Steve Block44f0eee2011-05-26 01:26:41 +0100589 ? isolate_->builtins()->builtin(Builtins::kNotifyDeoptimized)
590 : isolate_->builtins()->builtin(Builtins::kNotifyLazyDeoptimized);
Steve Block1e0659c2011-05-24 12:43:12 +0100591 output_frame->SetContinuation(
592 reinterpret_cast<intptr_t>(continuation->entry()));
593 }
594
595 if (output_count_ - 1 == frame_index) iterator->Done();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100596}
597
598
Steve Block1e0659c2011-05-24 12:43:12 +0100599#define __ masm()->
600
Ben Murdochb0fe1622011-05-05 13:52:32 +0100601void Deoptimizer::EntryGenerator::Generate() {
Steve Block1e0659c2011-05-24 12:43:12 +0100602 GeneratePrologue();
603 CpuFeatures::Scope scope(SSE2);
604
605 // Save all general purpose registers before messing with them.
606 const int kNumberOfRegisters = Register::kNumRegisters;
607
608 const int kDoubleRegsSize = kDoubleSize *
609 XMMRegister::kNumAllocatableRegisters;
610 __ subq(rsp, Immediate(kDoubleRegsSize));
611
612 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
613 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
614 int offset = i * kDoubleSize;
615 __ movsd(Operand(rsp, offset), xmm_reg);
616 }
617
618 // We push all registers onto the stack, even though we do not need
619 // to restore all later.
620 for (int i = 0; i < kNumberOfRegisters; i++) {
621 Register r = Register::toRegister(i);
622 __ push(r);
623 }
624
625 const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
626 kDoubleRegsSize;
627
628 // When calling new_deoptimizer_function we need to pass the last argument
629 // on the stack on windows and in r8 on linux. The remaining arguments are
630 // all passed in registers (different ones on linux and windows though).
631
632#ifdef _WIN64
633 Register arg4 = r9;
634 Register arg3 = r8;
635 Register arg2 = rdx;
636 Register arg1 = rcx;
637#else
638 Register arg4 = rcx;
639 Register arg3 = rdx;
640 Register arg2 = rsi;
641 Register arg1 = rdi;
642#endif
643
644 // We use this to keep the value of the fifth argument temporarily.
645 // Unfortunately we can't store it directly in r8 (used for passing
646 // this on linux), since it is another parameter passing register on windows.
647 Register arg5 = r11;
648
649 // Get the bailout id from the stack.
650 __ movq(arg3, Operand(rsp, kSavedRegistersAreaSize));
651
652 // Get the address of the location in the code object if possible
653 // and compute the fp-to-sp delta in register arg5.
654 if (type() == EAGER) {
655 __ Set(arg4, 0);
656 __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
657 } else {
658 __ movq(arg4, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
659 __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 2 * kPointerSize));
660 }
661
662 __ subq(arg5, rbp);
663 __ neg(arg5);
664
665 // Allocate a new deoptimizer object.
666 __ PrepareCallCFunction(5);
667 __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
668 __ movq(arg1, rax);
669 __ movq(arg2, Immediate(type()));
670 // Args 3 and 4 are already in the right registers.
671
672 // On windows put the argument on the stack (PrepareCallCFunction have
673 // created space for this). On linux pass the argument in r8.
674#ifdef _WIN64
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100675 __ movq(Operand(rsp, 4 * kPointerSize), arg5);
Steve Block1e0659c2011-05-24 12:43:12 +0100676#else
677 __ movq(r8, arg5);
678#endif
679
Steve Block44f0eee2011-05-26 01:26:41 +0100680 Isolate* isolate = masm()->isolate();
681
682 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 5);
Steve Block1e0659c2011-05-24 12:43:12 +0100683 // Preserve deoptimizer object in register rax and get the input
684 // frame descriptor pointer.
685 __ movq(rbx, Operand(rax, Deoptimizer::input_offset()));
686
687 // Fill in the input registers.
688 for (int i = kNumberOfRegisters -1; i >= 0; i--) {
689 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
690 __ pop(Operand(rbx, offset));
691 }
692
693 // Fill in the double input registers.
694 int double_regs_offset = FrameDescription::double_registers_offset();
695 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
696 int dst_offset = i * kDoubleSize + double_regs_offset;
697 __ pop(Operand(rbx, dst_offset));
698 }
699
700 // Remove the bailout id from the stack.
701 if (type() == EAGER) {
702 __ addq(rsp, Immediate(kPointerSize));
703 } else {
704 __ addq(rsp, Immediate(2 * kPointerSize));
705 }
706
707 // Compute a pointer to the unwinding limit in register rcx; that is
708 // the first stack slot not part of the input frame.
709 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
710 __ addq(rcx, rsp);
711
712 // Unwind the stack down to - but not including - the unwinding
713 // limit and copy the contents of the activation frame to the input
714 // frame description.
715 __ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
716 Label pop_loop;
717 __ bind(&pop_loop);
718 __ pop(Operand(rdx, 0));
719 __ addq(rdx, Immediate(sizeof(intptr_t)));
720 __ cmpq(rcx, rsp);
721 __ j(not_equal, &pop_loop);
722
723 // Compute the output frame in the deoptimizer.
724 __ push(rax);
725 __ PrepareCallCFunction(1);
726 __ movq(arg1, rax);
Steve Block44f0eee2011-05-26 01:26:41 +0100727 __ CallCFunction(
728 ExternalReference::compute_output_frames_function(isolate), 1);
Steve Block1e0659c2011-05-24 12:43:12 +0100729 __ pop(rax);
730
731 // Replace the current frame with the output frames.
732 Label outer_push_loop, inner_push_loop;
733 // Outer loop state: rax = current FrameDescription**, rdx = one past the
734 // last FrameDescription**.
735 __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
736 __ movq(rax, Operand(rax, Deoptimizer::output_offset()));
737 __ lea(rdx, Operand(rax, rdx, times_8, 0));
738 __ bind(&outer_push_loop);
739 // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
740 __ movq(rbx, Operand(rax, 0));
741 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
742 __ bind(&inner_push_loop);
743 __ subq(rcx, Immediate(sizeof(intptr_t)));
744 __ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
745 __ testq(rcx, rcx);
746 __ j(not_zero, &inner_push_loop);
747 __ addq(rax, Immediate(kPointerSize));
748 __ cmpq(rax, rdx);
749 __ j(below, &outer_push_loop);
750
751 // In case of OSR, we have to restore the XMM registers.
752 if (type() == OSR) {
753 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
754 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
755 int src_offset = i * kDoubleSize + double_regs_offset;
756 __ movsd(xmm_reg, Operand(rbx, src_offset));
757 }
758 }
759
760 // Push state, pc, and continuation from the last output frame.
761 if (type() != OSR) {
762 __ push(Operand(rbx, FrameDescription::state_offset()));
763 }
764 __ push(Operand(rbx, FrameDescription::pc_offset()));
765 __ push(Operand(rbx, FrameDescription::continuation_offset()));
766
767 // Push the registers from the last output frame.
768 for (int i = 0; i < kNumberOfRegisters; i++) {
769 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
770 __ push(Operand(rbx, offset));
771 }
772
773 // Restore the registers from the stack.
774 for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
775 Register r = Register::toRegister(i);
776 // Do not restore rsp, simply pop the value into the next register
777 // and overwrite this afterwards.
778 if (r.is(rsp)) {
779 ASSERT(i > 0);
780 r = Register::toRegister(i - 1);
781 }
782 __ pop(r);
783 }
784
785 // Set up the roots register.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100786 __ InitializeRootRegister();
787 __ InitializeSmiConstantRegister();
Steve Block1e0659c2011-05-24 12:43:12 +0100788
789 // Return to the continuation point.
790 __ ret(0);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100791}
792
793
794void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
Steve Block1e0659c2011-05-24 12:43:12 +0100795 // Create a sequence of deoptimization entries.
796 Label done;
797 for (int i = 0; i < count(); i++) {
798 int start = masm()->pc_offset();
799 USE(start);
800 __ push_imm32(i);
801 __ jmp(&done);
802 ASSERT(masm()->pc_offset() - start == table_entry_size_);
803 }
804 __ bind(&done);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100805}
806
Steve Block1e0659c2011-05-24 12:43:12 +0100807#undef __
808
809
Ben Murdochb0fe1622011-05-05 13:52:32 +0100810} } // namespace v8::internal
Ben Murdochb8e0da22011-05-16 14:20:40 +0100811
812#endif // V8_TARGET_ARCH_X64