1 // Copyright 2011 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 defined(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 int Deoptimizer::table_entry_size_ = 10; 42 43 44 int Deoptimizer::patch_size() { 45 return MacroAssembler::kCallInstructionLength; 46 } 47 48 49 #ifdef DEBUG 50 // Overwrites code with int3 instructions. 51 static 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. 64 class 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 104 void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) { 105 // TODO(1276): Implement. 106 } 107 108 109 void Deoptimizer::DeoptimizeFunction(JSFunction* function) { 110 HandleScope scope; 111 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); 195 DeoptimizerData* data = code->GetIsolate()->deoptimizer_data(); 196 node->set_next(data->deoptimizing_code_list_); 197 data->deoptimizing_code_list_ = node; 198 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)); 206 #ifdef DEBUG 207 if (FLAG_print_code) { 208 code->PrintLn(); 209 } 210 #endif 211 } 212 } 213 214 215 void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, 216 Code* check_code, 217 Code* replacement_code) { 218 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()); 245 } 246 247 248 void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, 249 Code* check_code, 250 Code* replacement_code) { 251 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 266 static 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; 281 } 282 283 284 void Deoptimizer::DoComputeOsrOutputFrame() { 285 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. 364 for (int i = StandardFrameConstants::kCallerPCOffset; 365 ok && i >= StandardFrameConstants::kMarkerOffset; 366 i -= kPointerSize) { 367 intptr_t input_value = input_->GetFrameSlot(input_offset); 368 if (FLAG_trace_osr) { 369 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", 386 output_offset, 387 input_value, 388 input_offset, 389 name); 390 } 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 } 416 Code* continuation = 417 function->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR); 418 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 } 428 } 429 430 431 void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, 432 int frame_index) { 433 // 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 533 // 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. 536 output_offset -= kPointerSize; 537 input_offset -= kPointerSize; 538 if (is_bottommost) { 539 value = input_->GetFrameSlot(input_offset); 540 } else { 541 value = reinterpret_cast<intptr_t>(function->context()); 542 } 543 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) 589 ? isolate_->builtins()->builtin(Builtins::kNotifyDeoptimized) 590 : isolate_->builtins()->builtin(Builtins::kNotifyLazyDeoptimized); 591 output_frame->SetContinuation( 592 reinterpret_cast<intptr_t>(continuation->entry())); 593 } 594 595 if (output_count_ - 1 == frame_index) iterator->Done(); 596 } 597 598 599 #define __ masm()-> 600 601 void Deoptimizer::EntryGenerator::Generate() { 602 GeneratePrologue(); 603 604 // Save all general purpose registers before messing with them. 605 const int kNumberOfRegisters = Register::kNumRegisters; 606 607 const int kDoubleRegsSize = kDoubleSize * 608 XMMRegister::kNumAllocatableRegisters; 609 __ subq(rsp, Immediate(kDoubleRegsSize)); 610 611 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) { 612 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i); 613 int offset = i * kDoubleSize; 614 __ movsd(Operand(rsp, offset), xmm_reg); 615 } 616 617 // We push all registers onto the stack, even though we do not need 618 // to restore all later. 619 for (int i = 0; i < kNumberOfRegisters; i++) { 620 Register r = Register::toRegister(i); 621 __ push(r); 622 } 623 624 const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize + 625 kDoubleRegsSize; 626 627 // When calling new_deoptimizer_function we need to pass the last argument 628 // on the stack on windows and in r8 on linux. The remaining arguments are 629 // all passed in registers (different ones on linux and windows though). 630 631 #ifdef _WIN64 632 Register arg4 = r9; 633 Register arg3 = r8; 634 Register arg2 = rdx; 635 Register arg1 = rcx; 636 #else 637 Register arg4 = rcx; 638 Register arg3 = rdx; 639 Register arg2 = rsi; 640 Register arg1 = rdi; 641 #endif 642 643 // We use this to keep the value of the fifth argument temporarily. 644 // Unfortunately we can't store it directly in r8 (used for passing 645 // this on linux), since it is another parameter passing register on windows. 646 Register arg5 = r11; 647 648 // Get the bailout id from the stack. 649 __ movq(arg3, Operand(rsp, kSavedRegistersAreaSize)); 650 651 // Get the address of the location in the code object if possible 652 // and compute the fp-to-sp delta in register arg5. 653 if (type() == EAGER) { 654 __ Set(arg4, 0); 655 __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize)); 656 } else { 657 __ movq(arg4, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize)); 658 __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 2 * kPointerSize)); 659 } 660 661 __ subq(arg5, rbp); 662 __ neg(arg5); 663 664 // Allocate a new deoptimizer object. 665 __ PrepareCallCFunction(6); 666 __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 667 __ movq(arg1, rax); 668 __ Set(arg2, type()); 669 // Args 3 and 4 are already in the right registers. 670 671 // On windows put the arguments on the stack (PrepareCallCFunction 672 // has created space for this). On linux pass the arguments in r8 and r9. 673 #ifdef _WIN64 674 __ movq(Operand(rsp, 4 * kPointerSize), arg5); 675 __ LoadAddress(arg5, ExternalReference::isolate_address()); 676 __ movq(Operand(rsp, 5 * kPointerSize), arg5); 677 #else 678 __ movq(r8, arg5); 679 __ LoadAddress(r9, ExternalReference::isolate_address()); 680 #endif 681 682 Isolate* isolate = masm()->isolate(); 683 684 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); 685 // Preserve deoptimizer object in register rax and get the input 686 // frame descriptor pointer. 687 __ movq(rbx, Operand(rax, Deoptimizer::input_offset())); 688 689 // Fill in the input registers. 690 for (int i = kNumberOfRegisters -1; i >= 0; i--) { 691 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 692 __ pop(Operand(rbx, offset)); 693 } 694 695 // Fill in the double input registers. 696 int double_regs_offset = FrameDescription::double_registers_offset(); 697 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) { 698 int dst_offset = i * kDoubleSize + double_regs_offset; 699 __ pop(Operand(rbx, dst_offset)); 700 } 701 702 // Remove the bailout id from the stack. 703 if (type() == EAGER) { 704 __ addq(rsp, Immediate(kPointerSize)); 705 } else { 706 __ addq(rsp, Immediate(2 * kPointerSize)); 707 } 708 709 // Compute a pointer to the unwinding limit in register rcx; that is 710 // the first stack slot not part of the input frame. 711 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 712 __ addq(rcx, rsp); 713 714 // Unwind the stack down to - but not including - the unwinding 715 // limit and copy the contents of the activation frame to the input 716 // frame description. 717 __ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset())); 718 Label pop_loop; 719 __ bind(&pop_loop); 720 __ pop(Operand(rdx, 0)); 721 __ addq(rdx, Immediate(sizeof(intptr_t))); 722 __ cmpq(rcx, rsp); 723 __ j(not_equal, &pop_loop); 724 725 // Compute the output frame in the deoptimizer. 726 __ push(rax); 727 __ PrepareCallCFunction(2); 728 __ movq(arg1, rax); 729 __ LoadAddress(arg2, ExternalReference::isolate_address()); 730 __ CallCFunction( 731 ExternalReference::compute_output_frames_function(isolate), 2); 732 __ pop(rax); 733 734 // Replace the current frame with the output frames. 735 Label outer_push_loop, inner_push_loop; 736 // Outer loop state: rax = current FrameDescription**, rdx = one past the 737 // last FrameDescription**. 738 __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset())); 739 __ movq(rax, Operand(rax, Deoptimizer::output_offset())); 740 __ lea(rdx, Operand(rax, rdx, times_8, 0)); 741 __ bind(&outer_push_loop); 742 // Inner loop state: rbx = current FrameDescription*, rcx = loop index. 743 __ movq(rbx, Operand(rax, 0)); 744 __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 745 __ bind(&inner_push_loop); 746 __ subq(rcx, Immediate(sizeof(intptr_t))); 747 __ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset())); 748 __ testq(rcx, rcx); 749 __ j(not_zero, &inner_push_loop); 750 __ addq(rax, Immediate(kPointerSize)); 751 __ cmpq(rax, rdx); 752 __ j(below, &outer_push_loop); 753 754 // In case of OSR, we have to restore the XMM registers. 755 if (type() == OSR) { 756 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) { 757 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i); 758 int src_offset = i * kDoubleSize + double_regs_offset; 759 __ movsd(xmm_reg, Operand(rbx, src_offset)); 760 } 761 } 762 763 // Push state, pc, and continuation from the last output frame. 764 if (type() != OSR) { 765 __ push(Operand(rbx, FrameDescription::state_offset())); 766 } 767 __ push(Operand(rbx, FrameDescription::pc_offset())); 768 __ push(Operand(rbx, FrameDescription::continuation_offset())); 769 770 // Push the registers from the last output frame. 771 for (int i = 0; i < kNumberOfRegisters; i++) { 772 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 773 __ push(Operand(rbx, offset)); 774 } 775 776 // Restore the registers from the stack. 777 for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) { 778 Register r = Register::toRegister(i); 779 // Do not restore rsp, simply pop the value into the next register 780 // and overwrite this afterwards. 781 if (r.is(rsp)) { 782 ASSERT(i > 0); 783 r = Register::toRegister(i - 1); 784 } 785 __ pop(r); 786 } 787 788 // Set up the roots register. 789 __ InitializeRootRegister(); 790 __ InitializeSmiConstantRegister(); 791 792 // Return to the continuation point. 793 __ ret(0); 794 } 795 796 797 void Deoptimizer::TableEntryGenerator::GeneratePrologue() { 798 // Create a sequence of deoptimization entries. 799 Label done; 800 for (int i = 0; i < count(); i++) { 801 int start = masm()->pc_offset(); 802 USE(start); 803 __ push_imm32(i); 804 __ jmp(&done); 805 ASSERT(masm()->pc_offset() - start == table_entry_size_); 806 } 807 __ bind(&done); 808 } 809 810 #undef __ 811 812 813 } } // namespace v8::internal 814 815 #endif // V8_TARGET_ARCH_X64 816