deoptimizer-arm.cc revision 3fb3ca8c7ca439d408449a395897395c0faae8d1
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#include "codegen.h" 31#include "deoptimizer.h" 32#include "full-codegen.h" 33#include "safepoint-table.h" 34 35namespace v8 { 36namespace internal { 37 38int Deoptimizer::table_entry_size_ = 16; 39 40 41int Deoptimizer::patch_size() { 42 const int kCallInstructionSizeInWords = 3; 43 return kCallInstructionSizeInWords * Assembler::kInstrSize; 44} 45 46 47void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) { 48 // Nothing to do. No new relocation information is written for lazy 49 // deoptimization on ARM. 50} 51 52 53void Deoptimizer::DeoptimizeFunction(JSFunction* function) { 54 HandleScope scope; 55 AssertNoAllocation no_allocation; 56 57 if (!function->IsOptimized()) return; 58 59 // Get the optimized code. 60 Code* code = function->code(); 61 62 // Invalidate the relocation information, as it will become invalid by the 63 // code patching below, and is not needed any more. 64 code->InvalidateRelocation(); 65 66 // For each return after a safepoint insert an absolute call to the 67 // corresponding deoptimization entry. 68 unsigned last_pc_offset = 0; 69 SafepointTable table(function->code()); 70 for (unsigned i = 0; i < table.length(); i++) { 71 unsigned pc_offset = table.GetPcOffset(i); 72 SafepointEntry safepoint_entry = table.GetEntry(i); 73 int deoptimization_index = safepoint_entry.deoptimization_index(); 74 int gap_code_size = safepoint_entry.gap_code_size(); 75 // Check that we did not shoot past next safepoint. 76 CHECK(pc_offset >= last_pc_offset); 77#ifdef DEBUG 78 // Destroy the code which is not supposed to be run again. 79 int instructions = (pc_offset - last_pc_offset) / Assembler::kInstrSize; 80 CodePatcher destroyer(code->instruction_start() + last_pc_offset, 81 instructions); 82 for (int x = 0; x < instructions; x++) { 83 destroyer.masm()->bkpt(0); 84 } 85#endif 86 last_pc_offset = pc_offset; 87 if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) { 88 Address deoptimization_entry = Deoptimizer::GetDeoptimizationEntry( 89 deoptimization_index, Deoptimizer::LAZY); 90 last_pc_offset += gap_code_size; 91 int call_size_in_bytes = MacroAssembler::CallSize(deoptimization_entry, 92 RelocInfo::NONE); 93 int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize; 94 ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0); 95 ASSERT(call_size_in_bytes <= patch_size()); 96 CodePatcher patcher(code->instruction_start() + last_pc_offset, 97 call_size_in_words); 98 patcher.masm()->Call(deoptimization_entry, RelocInfo::NONE); 99 last_pc_offset += call_size_in_bytes; 100 } 101 } 102 103 104#ifdef DEBUG 105 // Destroy the code which is not supposed to be run again. 106 int instructions = 107 (code->safepoint_table_offset() - last_pc_offset) / Assembler::kInstrSize; 108 CodePatcher destroyer(code->instruction_start() + last_pc_offset, 109 instructions); 110 for (int x = 0; x < instructions; x++) { 111 destroyer.masm()->bkpt(0); 112 } 113#endif 114 115 // Add the deoptimizing code to the list. 116 DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); 117 DeoptimizerData* data = code->GetIsolate()->deoptimizer_data(); 118 node->set_next(data->deoptimizing_code_list_); 119 data->deoptimizing_code_list_ = node; 120 121 // Set the code for the function to non-optimized version. 122 function->ReplaceCode(function->shared()->code()); 123 124 if (FLAG_trace_deopt) { 125 PrintF("[forced deoptimization: "); 126 function->PrintName(); 127 PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function)); 128#ifdef DEBUG 129 if (FLAG_print_code) { 130 code->PrintLn(); 131 } 132#endif 133 } 134} 135 136 137void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, 138 Code* check_code, 139 Code* replacement_code) { 140 const int kInstrSize = Assembler::kInstrSize; 141 // The call of the stack guard check has the following form: 142 // e1 5d 00 0c cmp sp, <limit> 143 // 2a 00 00 01 bcs ok 144 // e5 9f c? ?? ldr ip, [pc, <stack guard address>] 145 // e1 2f ff 3c blx ip 146 ASSERT(Memory::int32_at(pc_after - kInstrSize) == 147 (al | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | BLX | ip.code())); 148 ASSERT(Assembler::IsLdrPcImmediateOffset( 149 Assembler::instr_at(pc_after - 2 * kInstrSize))); 150 151 // We patch the code to the following form: 152 // e1 5d 00 0c cmp sp, <limit> 153 // e1 a0 00 00 mov r0, r0 (NOP) 154 // e5 9f c? ?? ldr ip, [pc, <on-stack replacement address>] 155 // e1 2f ff 3c blx ip 156 // and overwrite the constant containing the 157 // address of the stack check stub. 158 159 // Replace conditional jump with NOP. 160 CodePatcher patcher(pc_after - 3 * kInstrSize, 1); 161 patcher.masm()->nop(); 162 163 // Replace the stack check address in the constant pool 164 // with the entry address of the replacement code. 165 uint32_t stack_check_address_offset = Memory::uint16_at(pc_after - 166 2 * kInstrSize) & 0xfff; 167 Address stack_check_address_pointer = pc_after + stack_check_address_offset; 168 ASSERT(Memory::uint32_at(stack_check_address_pointer) == 169 reinterpret_cast<uint32_t>(check_code->entry())); 170 Memory::uint32_at(stack_check_address_pointer) = 171 reinterpret_cast<uint32_t>(replacement_code->entry()); 172} 173 174 175void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, 176 Code* check_code, 177 Code* replacement_code) { 178 const int kInstrSize = Assembler::kInstrSize; 179 ASSERT(Memory::uint32_at(pc_after - kInstrSize) == 0xe12fff3c); 180 ASSERT(Memory::uint8_at(pc_after - kInstrSize - 1) == 0xe5); 181 ASSERT(Memory::uint8_at(pc_after - kInstrSize - 2) == 0x9f); 182 183 // Replace NOP with conditional jump. 184 CodePatcher patcher(pc_after - 3 * kInstrSize, 1); 185 patcher.masm()->b(+4, cs); 186 187 // Replace the stack check address in the constant pool 188 // with the entry address of the replacement code. 189 uint32_t stack_check_address_offset = Memory::uint16_at(pc_after - 190 2 * kInstrSize) & 0xfff; 191 Address stack_check_address_pointer = pc_after + stack_check_address_offset; 192 ASSERT(Memory::uint32_at(stack_check_address_pointer) == 193 reinterpret_cast<uint32_t>(replacement_code->entry())); 194 Memory::uint32_at(stack_check_address_pointer) = 195 reinterpret_cast<uint32_t>(check_code->entry()); 196} 197 198 199static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) { 200 ByteArray* translations = data->TranslationByteArray(); 201 int length = data->DeoptCount(); 202 for (int i = 0; i < length; i++) { 203 if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) { 204 TranslationIterator it(translations, data->TranslationIndex(i)->value()); 205 int value = it.Next(); 206 ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value)); 207 // Read the number of frames. 208 value = it.Next(); 209 if (value == 1) return i; 210 } 211 } 212 UNREACHABLE(); 213 return -1; 214} 215 216 217void Deoptimizer::DoComputeOsrOutputFrame() { 218 DeoptimizationInputData* data = DeoptimizationInputData::cast( 219 optimized_code_->deoptimization_data()); 220 unsigned ast_id = data->OsrAstId()->value(); 221 222 int bailout_id = LookupBailoutId(data, ast_id); 223 unsigned translation_index = data->TranslationIndex(bailout_id)->value(); 224 ByteArray* translations = data->TranslationByteArray(); 225 226 TranslationIterator iterator(translations, translation_index); 227 Translation::Opcode opcode = 228 static_cast<Translation::Opcode>(iterator.Next()); 229 ASSERT(Translation::BEGIN == opcode); 230 USE(opcode); 231 int count = iterator.Next(); 232 ASSERT(count == 1); 233 USE(count); 234 235 opcode = static_cast<Translation::Opcode>(iterator.Next()); 236 USE(opcode); 237 ASSERT(Translation::FRAME == opcode); 238 unsigned node_id = iterator.Next(); 239 USE(node_id); 240 ASSERT(node_id == ast_id); 241 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator.Next())); 242 USE(function); 243 ASSERT(function == function_); 244 unsigned height = iterator.Next(); 245 unsigned height_in_bytes = height * kPointerSize; 246 USE(height_in_bytes); 247 248 unsigned fixed_size = ComputeFixedSize(function_); 249 unsigned input_frame_size = input_->GetFrameSize(); 250 ASSERT(fixed_size + height_in_bytes == input_frame_size); 251 252 unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize; 253 unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); 254 unsigned outgoing_size = outgoing_height * kPointerSize; 255 unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; 256 ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. 257 258 if (FLAG_trace_osr) { 259 PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", 260 reinterpret_cast<intptr_t>(function_)); 261 function_->PrintName(); 262 PrintF(" => node=%u, frame=%d->%d]\n", 263 ast_id, 264 input_frame_size, 265 output_frame_size); 266 } 267 268 // There's only one output frame in the OSR case. 269 output_count_ = 1; 270 output_ = new FrameDescription*[1]; 271 output_[0] = new(output_frame_size) FrameDescription( 272 output_frame_size, function_); 273#ifdef DEBUG 274 output_[0]->SetKind(Code::OPTIMIZED_FUNCTION); 275#endif 276 277 // Clear the incoming parameters in the optimized frame to avoid 278 // confusing the garbage collector. 279 unsigned output_offset = output_frame_size - kPointerSize; 280 int parameter_count = function_->shared()->formal_parameter_count() + 1; 281 for (int i = 0; i < parameter_count; ++i) { 282 output_[0]->SetFrameSlot(output_offset, 0); 283 output_offset -= kPointerSize; 284 } 285 286 // Translate the incoming parameters. This may overwrite some of the 287 // incoming argument slots we've just cleared. 288 int input_offset = input_frame_size - kPointerSize; 289 bool ok = true; 290 int limit = input_offset - (parameter_count * kPointerSize); 291 while (ok && input_offset > limit) { 292 ok = DoOsrTranslateCommand(&iterator, &input_offset); 293 } 294 295 // There are no translation commands for the caller's pc and fp, the 296 // context, and the function. Set them up explicitly. 297 for (int i = StandardFrameConstants::kCallerPCOffset; 298 ok && i >= StandardFrameConstants::kMarkerOffset; 299 i -= kPointerSize) { 300 uint32_t input_value = input_->GetFrameSlot(input_offset); 301 if (FLAG_trace_osr) { 302 const char* name = "UNKNOWN"; 303 switch (i) { 304 case StandardFrameConstants::kCallerPCOffset: 305 name = "caller's pc"; 306 break; 307 case StandardFrameConstants::kCallerFPOffset: 308 name = "fp"; 309 break; 310 case StandardFrameConstants::kContextOffset: 311 name = "context"; 312 break; 313 case StandardFrameConstants::kMarkerOffset: 314 name = "function"; 315 break; 316 } 317 PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n", 318 output_offset, 319 input_value, 320 input_offset, 321 name); 322 } 323 324 output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); 325 input_offset -= kPointerSize; 326 output_offset -= kPointerSize; 327 } 328 329 // Translate the rest of the frame. 330 while (ok && input_offset >= 0) { 331 ok = DoOsrTranslateCommand(&iterator, &input_offset); 332 } 333 334 // If translation of any command failed, continue using the input frame. 335 if (!ok) { 336 delete output_[0]; 337 output_[0] = input_; 338 output_[0]->SetPc(reinterpret_cast<uint32_t>(from_)); 339 } else { 340 // Setup the frame pointer and the context pointer. 341 output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code())); 342 output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code())); 343 344 unsigned pc_offset = data->OsrPcOffset()->value(); 345 uint32_t pc = reinterpret_cast<uint32_t>( 346 optimized_code_->entry() + pc_offset); 347 output_[0]->SetPc(pc); 348 } 349 Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR); 350 output_[0]->SetContinuation( 351 reinterpret_cast<uint32_t>(continuation->entry())); 352 353 if (FLAG_trace_osr) { 354 PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", 355 ok ? "finished" : "aborted", 356 reinterpret_cast<intptr_t>(function)); 357 function->PrintName(); 358 PrintF(" => pc=0x%0x]\n", output_[0]->GetPc()); 359 } 360} 361 362 363// This code is very similar to ia32 code, but relies on register names (fp, sp) 364// and how the frame is laid out. 365void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, 366 int frame_index) { 367 // Read the ast node id, function, and frame height for this output frame. 368 Translation::Opcode opcode = 369 static_cast<Translation::Opcode>(iterator->Next()); 370 USE(opcode); 371 ASSERT(Translation::FRAME == opcode); 372 int node_id = iterator->Next(); 373 JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); 374 unsigned height = iterator->Next(); 375 unsigned height_in_bytes = height * kPointerSize; 376 if (FLAG_trace_deopt) { 377 PrintF(" translating "); 378 function->PrintName(); 379 PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes); 380 } 381 382 // The 'fixed' part of the frame consists of the incoming parameters and 383 // the part described by JavaScriptFrameConstants. 384 unsigned fixed_frame_size = ComputeFixedSize(function); 385 unsigned input_frame_size = input_->GetFrameSize(); 386 unsigned output_frame_size = height_in_bytes + fixed_frame_size; 387 388 // Allocate and store the output frame description. 389 FrameDescription* output_frame = 390 new(output_frame_size) FrameDescription(output_frame_size, function); 391#ifdef DEBUG 392 output_frame->SetKind(Code::FUNCTION); 393#endif 394 395 bool is_bottommost = (0 == frame_index); 396 bool is_topmost = (output_count_ - 1 == frame_index); 397 ASSERT(frame_index >= 0 && frame_index < output_count_); 398 ASSERT(output_[frame_index] == NULL); 399 output_[frame_index] = output_frame; 400 401 // The top address for the bottommost output frame can be computed from 402 // the input frame pointer and the output frame's height. For all 403 // subsequent output frames, it can be computed from the previous one's 404 // top address and the current frame's size. 405 uint32_t top_address; 406 if (is_bottommost) { 407 // 2 = context and function in the frame. 408 top_address = 409 input_->GetRegister(fp.code()) - (2 * kPointerSize) - height_in_bytes; 410 } else { 411 top_address = output_[frame_index - 1]->GetTop() - output_frame_size; 412 } 413 output_frame->SetTop(top_address); 414 415 // Compute the incoming parameter translation. 416 int parameter_count = function->shared()->formal_parameter_count() + 1; 417 unsigned output_offset = output_frame_size; 418 unsigned input_offset = input_frame_size; 419 for (int i = 0; i < parameter_count; ++i) { 420 output_offset -= kPointerSize; 421 DoTranslateCommand(iterator, frame_index, output_offset); 422 } 423 input_offset -= (parameter_count * kPointerSize); 424 425 // There are no translation commands for the caller's pc and fp, the 426 // context, and the function. Synthesize their values and set them up 427 // explicitly. 428 // 429 // The caller's pc for the bottommost output frame is the same as in the 430 // input frame. For all subsequent output frames, it can be read from the 431 // previous one. This frame's pc can be computed from the non-optimized 432 // function code and AST id of the bailout. 433 output_offset -= kPointerSize; 434 input_offset -= kPointerSize; 435 intptr_t value; 436 if (is_bottommost) { 437 value = input_->GetFrameSlot(input_offset); 438 } else { 439 value = output_[frame_index - 1]->GetPc(); 440 } 441 output_frame->SetFrameSlot(output_offset, value); 442 if (FLAG_trace_deopt) { 443 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n", 444 top_address + output_offset, output_offset, value); 445 } 446 447 // The caller's frame pointer for the bottommost output frame is the same 448 // as in the input frame. For all subsequent output frames, it can be 449 // read from the previous one. Also compute and set this frame's frame 450 // pointer. 451 output_offset -= kPointerSize; 452 input_offset -= kPointerSize; 453 if (is_bottommost) { 454 value = input_->GetFrameSlot(input_offset); 455 } else { 456 value = output_[frame_index - 1]->GetFp(); 457 } 458 output_frame->SetFrameSlot(output_offset, value); 459 intptr_t fp_value = top_address + output_offset; 460 ASSERT(!is_bottommost || input_->GetRegister(fp.code()) == fp_value); 461 output_frame->SetFp(fp_value); 462 if (is_topmost) { 463 output_frame->SetRegister(fp.code(), fp_value); 464 } 465 if (FLAG_trace_deopt) { 466 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n", 467 fp_value, output_offset, value); 468 } 469 470 // For the bottommost output frame the context can be gotten from the input 471 // frame. For all subsequent output frames it can be gotten from the function 472 // so long as we don't inline functions that need local contexts. 473 output_offset -= kPointerSize; 474 input_offset -= kPointerSize; 475 if (is_bottommost) { 476 value = input_->GetFrameSlot(input_offset); 477 } else { 478 value = reinterpret_cast<intptr_t>(function->context()); 479 } 480 output_frame->SetFrameSlot(output_offset, value); 481 if (is_topmost) { 482 output_frame->SetRegister(cp.code(), value); 483 } 484 if (FLAG_trace_deopt) { 485 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n", 486 top_address + output_offset, output_offset, value); 487 } 488 489 // The function was mentioned explicitly in the BEGIN_FRAME. 490 output_offset -= kPointerSize; 491 input_offset -= kPointerSize; 492 value = reinterpret_cast<uint32_t>(function); 493 // The function for the bottommost output frame should also agree with the 494 // input frame. 495 ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value); 496 output_frame->SetFrameSlot(output_offset, value); 497 if (FLAG_trace_deopt) { 498 PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n", 499 top_address + output_offset, output_offset, value); 500 } 501 502 // Translate the rest of the frame. 503 for (unsigned i = 0; i < height; ++i) { 504 output_offset -= kPointerSize; 505 DoTranslateCommand(iterator, frame_index, output_offset); 506 } 507 ASSERT(0 == output_offset); 508 509 // Compute this frame's PC, state, and continuation. 510 Code* non_optimized_code = function->shared()->code(); 511 FixedArray* raw_data = non_optimized_code->deoptimization_data(); 512 DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data); 513 Address start = non_optimized_code->instruction_start(); 514 unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared()); 515 unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state); 516 uint32_t pc_value = reinterpret_cast<uint32_t>(start + pc_offset); 517 output_frame->SetPc(pc_value); 518 if (is_topmost) { 519 output_frame->SetRegister(pc.code(), pc_value); 520 } 521 522 FullCodeGenerator::State state = 523 FullCodeGenerator::StateField::decode(pc_and_state); 524 output_frame->SetState(Smi::FromInt(state)); 525 526 527 // Set the continuation for the topmost frame. 528 if (is_topmost && bailout_type_ != DEBUGGER) { 529 Builtins* builtins = isolate_->builtins(); 530 Code* continuation = (bailout_type_ == EAGER) 531 ? builtins->builtin(Builtins::kNotifyDeoptimized) 532 : builtins->builtin(Builtins::kNotifyLazyDeoptimized); 533 output_frame->SetContinuation( 534 reinterpret_cast<uint32_t>(continuation->entry())); 535 } 536 537 if (output_count_ - 1 == frame_index) iterator->Done(); 538} 539 540 541void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { 542 // Set the register values. The values are not important as there are no 543 // callee saved registers in JavaScript frames, so all registers are 544 // spilled. Registers fp and sp are set to the correct values though. 545 546 for (int i = 0; i < Register::kNumRegisters; i++) { 547 input_->SetRegister(i, i * 4); 548 } 549 input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp())); 550 input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp())); 551 for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) { 552 input_->SetDoubleRegister(i, 0.0); 553 } 554 555 // Fill the frame content from the actual data on the frame. 556 for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { 557 input_->SetFrameSlot(i, Memory::uint32_at(tos + i)); 558 } 559} 560 561 562#define __ masm()-> 563 564// This code tries to be close to ia32 code so that any changes can be 565// easily ported. 566void Deoptimizer::EntryGenerator::Generate() { 567 GeneratePrologue(); 568 569 Isolate* isolate = masm()->isolate(); 570 571 CpuFeatures::Scope scope(VFP3); 572 // Save all general purpose registers before messing with them. 573 const int kNumberOfRegisters = Register::kNumRegisters; 574 575 // Everything but pc, lr and ip which will be saved but not restored. 576 RegList restored_regs = kJSCallerSaved | kCalleeSaved | ip.bit(); 577 578 const int kDoubleRegsSize = 579 kDoubleSize * DwVfpRegister::kNumAllocatableRegisters; 580 581 // Save all VFP registers before messing with them. 582 DwVfpRegister first = DwVfpRegister::FromAllocationIndex(0); 583 DwVfpRegister last = 584 DwVfpRegister::FromAllocationIndex( 585 DwVfpRegister::kNumAllocatableRegisters - 1); 586 ASSERT(last.code() > first.code()); 587 ASSERT((last.code() - first.code()) == 588 (DwVfpRegister::kNumAllocatableRegisters - 1)); 589#ifdef DEBUG 590 for (int i = 0; i <= (DwVfpRegister::kNumAllocatableRegisters - 1); i++) { 591 ASSERT((DwVfpRegister::FromAllocationIndex(i).code() <= last.code()) && 592 (DwVfpRegister::FromAllocationIndex(i).code() >= first.code())); 593 } 594#endif 595 __ vstm(db_w, sp, first, last); 596 597 // Push all 16 registers (needed to populate FrameDescription::registers_). 598 __ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit()); 599 600 const int kSavedRegistersAreaSize = 601 (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize; 602 603 // Get the bailout id from the stack. 604 __ ldr(r2, MemOperand(sp, kSavedRegistersAreaSize)); 605 606 // Get the address of the location in the code object if possible (r3) (return 607 // address for lazy deoptimization) and compute the fp-to-sp delta in 608 // register r4. 609 if (type() == EAGER) { 610 __ mov(r3, Operand(0)); 611 // Correct one word for bailout id. 612 __ add(r4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); 613 } else if (type() == OSR) { 614 __ mov(r3, lr); 615 // Correct one word for bailout id. 616 __ add(r4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); 617 } else { 618 __ mov(r3, lr); 619 // Correct two words for bailout id and return address. 620 __ add(r4, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize))); 621 } 622 __ sub(r4, fp, r4); 623 624 // Allocate a new deoptimizer object. 625 // Pass four arguments in r0 to r3 and fifth argument on stack. 626 __ PrepareCallCFunction(6, r5); 627 __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 628 __ mov(r1, Operand(type())); // bailout type, 629 // r2: bailout id already loaded. 630 // r3: code address or 0 already loaded. 631 __ str(r4, MemOperand(sp, 0 * kPointerSize)); // Fp-to-sp delta. 632 __ mov(r5, Operand(ExternalReference::isolate_address())); 633 __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate. 634 // Call Deoptimizer::New(). 635 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); 636 637 // Preserve "deoptimizer" object in register r0 and get the input 638 // frame descriptor pointer to r1 (deoptimizer->input_); 639 __ ldr(r1, MemOperand(r0, Deoptimizer::input_offset())); 640 641 // Copy core registers into FrameDescription::registers_[kNumRegisters]. 642 ASSERT(Register::kNumRegisters == kNumberOfRegisters); 643 for (int i = 0; i < kNumberOfRegisters; i++) { 644 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 645 __ ldr(r2, MemOperand(sp, i * kPointerSize)); 646 __ str(r2, MemOperand(r1, offset)); 647 } 648 649 // Copy VFP registers to 650 // double_registers_[DoubleRegister::kNumAllocatableRegisters] 651 int double_regs_offset = FrameDescription::double_registers_offset(); 652 for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; ++i) { 653 int dst_offset = i * kDoubleSize + double_regs_offset; 654 int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize; 655 __ vldr(d0, sp, src_offset); 656 __ vstr(d0, r1, dst_offset); 657 } 658 659 // Remove the bailout id, eventually return address, and the saved registers 660 // from the stack. 661 if (type() == EAGER || type() == OSR) { 662 __ add(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); 663 } else { 664 __ add(sp, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize))); 665 } 666 667 // Compute a pointer to the unwinding limit in register r2; that is 668 // the first stack slot not part of the input frame. 669 __ ldr(r2, MemOperand(r1, FrameDescription::frame_size_offset())); 670 __ add(r2, r2, sp); 671 672 // Unwind the stack down to - but not including - the unwinding 673 // limit and copy the contents of the activation frame to the input 674 // frame description. 675 __ add(r3, r1, Operand(FrameDescription::frame_content_offset())); 676 Label pop_loop; 677 __ bind(&pop_loop); 678 __ pop(r4); 679 __ str(r4, MemOperand(r3, 0)); 680 __ add(r3, r3, Operand(sizeof(uint32_t))); 681 __ cmp(r2, sp); 682 __ b(ne, &pop_loop); 683 684 // Compute the output frame in the deoptimizer. 685 __ push(r0); // Preserve deoptimizer object across call. 686 // r0: deoptimizer object; r1: scratch. 687 __ PrepareCallCFunction(1, r1); 688 // Call Deoptimizer::ComputeOutputFrames(). 689 __ CallCFunction( 690 ExternalReference::compute_output_frames_function(isolate), 1); 691 __ pop(r0); // Restore deoptimizer object (class Deoptimizer). 692 693 // Replace the current (input) frame with the output frames. 694 Label outer_push_loop, inner_push_loop; 695 // Outer loop state: r0 = current "FrameDescription** output_", 696 // r1 = one past the last FrameDescription**. 697 __ ldr(r1, MemOperand(r0, Deoptimizer::output_count_offset())); 698 __ ldr(r0, MemOperand(r0, Deoptimizer::output_offset())); // r0 is output_. 699 __ add(r1, r0, Operand(r1, LSL, 2)); 700 __ bind(&outer_push_loop); 701 // Inner loop state: r2 = current FrameDescription*, r3 = loop index. 702 __ ldr(r2, MemOperand(r0, 0)); // output_[ix] 703 __ ldr(r3, MemOperand(r2, FrameDescription::frame_size_offset())); 704 __ bind(&inner_push_loop); 705 __ sub(r3, r3, Operand(sizeof(uint32_t))); 706 // __ add(r6, r2, Operand(r3, LSL, 1)); 707 __ add(r6, r2, Operand(r3)); 708 __ ldr(r7, MemOperand(r6, FrameDescription::frame_content_offset())); 709 __ push(r7); 710 __ cmp(r3, Operand(0)); 711 __ b(ne, &inner_push_loop); // test for gt? 712 __ add(r0, r0, Operand(kPointerSize)); 713 __ cmp(r0, r1); 714 __ b(lt, &outer_push_loop); 715 716 // Push state, pc, and continuation from the last output frame. 717 if (type() != OSR) { 718 __ ldr(r6, MemOperand(r2, FrameDescription::state_offset())); 719 __ push(r6); 720 } 721 722 __ ldr(r6, MemOperand(r2, FrameDescription::pc_offset())); 723 __ push(r6); 724 __ ldr(r6, MemOperand(r2, FrameDescription::continuation_offset())); 725 __ push(r6); 726 727 // Push the registers from the last output frame. 728 for (int i = kNumberOfRegisters - 1; i >= 0; i--) { 729 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 730 __ ldr(r6, MemOperand(r2, offset)); 731 __ push(r6); 732 } 733 734 // Restore the registers from the stack. 735 __ ldm(ia_w, sp, restored_regs); // all but pc registers. 736 __ pop(ip); // remove sp 737 __ pop(ip); // remove lr 738 739 // Set up the roots register. 740 ExternalReference roots_address = ExternalReference::roots_address(isolate); 741 __ mov(r10, Operand(roots_address)); 742 743 __ pop(ip); // remove pc 744 __ pop(r7); // get continuation, leave pc on stack 745 __ pop(lr); 746 __ Jump(r7); 747 __ stop("Unreachable."); 748} 749 750 751void Deoptimizer::TableEntryGenerator::GeneratePrologue() { 752 // Create a sequence of deoptimization entries. Note that any 753 // registers may be still live. 754 Label done; 755 for (int i = 0; i < count(); i++) { 756 int start = masm()->pc_offset(); 757 USE(start); 758 if (type() == EAGER) { 759 __ nop(); 760 } else { 761 // Emulate ia32 like call by pushing return address to stack. 762 __ push(lr); 763 } 764 __ mov(ip, Operand(i)); 765 __ push(ip); 766 __ b(&done); 767 ASSERT(masm()->pc_offset() - start == table_entry_size_); 768 } 769 __ bind(&done); 770} 771 772#undef __ 773 774} } // namespace v8::internal 775