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