1// Copyright 2011 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/v8.h" 6 7#include "src/codegen.h" 8#include "src/deoptimizer.h" 9#include "src/full-codegen.h" 10#include "src/safepoint-table.h" 11 12namespace v8 { 13namespace internal { 14 15 16int Deoptimizer::patch_size() { 17 const int kCallInstructionSizeInWords = 6; 18 return kCallInstructionSizeInWords * Assembler::kInstrSize; 19} 20 21 22void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { 23 Address code_start_address = code->instruction_start(); 24 // Invalidate the relocation information, as it will become invalid by the 25 // code patching below, and is not needed any more. 26 code->InvalidateRelocation(); 27 28 if (FLAG_zap_code_space) { 29 // Fail hard and early if we enter this code object again. 30 byte* pointer = code->FindCodeAgeSequence(); 31 if (pointer != NULL) { 32 pointer += kNoCodeAgeSequenceLength; 33 } else { 34 pointer = code->instruction_start(); 35 } 36 CodePatcher patcher(pointer, 1); 37 patcher.masm()->break_(0xCC); 38 39 DeoptimizationInputData* data = 40 DeoptimizationInputData::cast(code->deoptimization_data()); 41 int osr_offset = data->OsrPcOffset()->value(); 42 if (osr_offset > 0) { 43 CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1); 44 osr_patcher.masm()->break_(0xCC); 45 } 46 } 47 48 DeoptimizationInputData* deopt_data = 49 DeoptimizationInputData::cast(code->deoptimization_data()); 50#ifdef DEBUG 51 Address prev_call_address = NULL; 52#endif 53 // For each LLazyBailout instruction insert a call to the corresponding 54 // deoptimization entry. 55 for (int i = 0; i < deopt_data->DeoptCount(); i++) { 56 if (deopt_data->Pc(i)->value() == -1) continue; 57 Address call_address = code_start_address + deopt_data->Pc(i)->value(); 58 Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY); 59 int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry, 60 RelocInfo::NONE32); 61 int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize; 62 DCHECK(call_size_in_bytes % Assembler::kInstrSize == 0); 63 DCHECK(call_size_in_bytes <= patch_size()); 64 CodePatcher patcher(call_address, call_size_in_words); 65 patcher.masm()->Call(deopt_entry, RelocInfo::NONE32); 66 DCHECK(prev_call_address == NULL || 67 call_address >= prev_call_address + patch_size()); 68 DCHECK(call_address + patch_size() <= code->instruction_end()); 69 70#ifdef DEBUG 71 prev_call_address = call_address; 72#endif 73 } 74} 75 76 77void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { 78 // Set the register values. The values are not important as there are no 79 // callee saved registers in JavaScript frames, so all registers are 80 // spilled. Registers fp and sp are set to the correct values though. 81 82 for (int i = 0; i < Register::kNumRegisters; i++) { 83 input_->SetRegister(i, i * 4); 84 } 85 input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp())); 86 input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp())); 87 for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) { 88 input_->SetDoubleRegister(i, 0.0); 89 } 90 91 // Fill the frame content from the actual data on the frame. 92 for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { 93 input_->SetFrameSlot(i, Memory::uint64_at(tos + i)); 94 } 95} 96 97 98void Deoptimizer::SetPlatformCompiledStubRegisters( 99 FrameDescription* output_frame, CodeStubDescriptor* descriptor) { 100 ApiFunction function(descriptor->deoptimization_handler()); 101 ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_); 102 intptr_t handler = reinterpret_cast<intptr_t>(xref.address()); 103 int params = descriptor->GetHandlerParameterCount(); 104 output_frame->SetRegister(s0.code(), params); 105 output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize); 106 output_frame->SetRegister(s2.code(), handler); 107} 108 109 110void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { 111 for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) { 112 double double_value = input_->GetDoubleRegister(i); 113 output_frame->SetDoubleRegister(i, double_value); 114 } 115} 116 117 118bool Deoptimizer::HasAlignmentPadding(JSFunction* function) { 119 // There is no dynamic alignment padding on MIPS in the input frame. 120 return false; 121} 122 123 124#define __ masm()-> 125 126 127// This code tries to be close to ia32 code so that any changes can be 128// easily ported. 129void Deoptimizer::EntryGenerator::Generate() { 130 GeneratePrologue(); 131 132 // Unlike on ARM we don't save all the registers, just the useful ones. 133 // For the rest, there are gaps on the stack, so the offsets remain the same. 134 const int kNumberOfRegisters = Register::kNumRegisters; 135 136 RegList restored_regs = kJSCallerSaved | kCalleeSaved; 137 RegList saved_regs = restored_regs | sp.bit() | ra.bit(); 138 139 const int kDoubleRegsSize = 140 kDoubleSize * FPURegister::kMaxNumAllocatableRegisters; 141 142 // Save all FPU registers before messing with them. 143 __ Dsubu(sp, sp, Operand(kDoubleRegsSize)); 144 for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) { 145 FPURegister fpu_reg = FPURegister::FromAllocationIndex(i); 146 int offset = i * kDoubleSize; 147 __ sdc1(fpu_reg, MemOperand(sp, offset)); 148 } 149 150 // Push saved_regs (needed to populate FrameDescription::registers_). 151 // Leave gaps for other registers. 152 __ Dsubu(sp, sp, kNumberOfRegisters * kPointerSize); 153 for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) { 154 if ((saved_regs & (1 << i)) != 0) { 155 __ sd(ToRegister(i), MemOperand(sp, kPointerSize * i)); 156 } 157 } 158 159 const int kSavedRegistersAreaSize = 160 (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize; 161 162 // Get the bailout id from the stack. 163 __ ld(a2, MemOperand(sp, kSavedRegistersAreaSize)); 164 165 // Get the address of the location in the code object (a3) (return 166 // address for lazy deoptimization) and compute the fp-to-sp delta in 167 // register a4. 168 __ mov(a3, ra); 169 // Correct one word for bailout id. 170 __ Daddu(a4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); 171 172 __ Dsubu(a4, fp, a4); 173 174 // Allocate a new deoptimizer object. 175 __ PrepareCallCFunction(6, a5); 176 // Pass six arguments, according to O32 or n64 ABI. a0..a3 are same for both. 177 __ li(a1, Operand(type())); // bailout type, 178 __ ld(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 179 // a2: bailout id already loaded. 180 // a3: code address or 0 already loaded. 181 if (kMipsAbi == kN64) { 182 // a4: already has fp-to-sp delta. 183 __ li(a5, Operand(ExternalReference::isolate_address(isolate()))); 184 } else { // O32 abi. 185 // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack. 186 __ sd(a4, CFunctionArgumentOperand(5)); // Fp-to-sp delta. 187 __ li(a5, Operand(ExternalReference::isolate_address(isolate()))); 188 __ sd(a5, CFunctionArgumentOperand(6)); // Isolate. 189 } 190 // Call Deoptimizer::New(). 191 { 192 AllowExternalCallThatCantCauseGC scope(masm()); 193 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); 194 } 195 196 // Preserve "deoptimizer" object in register v0 and get the input 197 // frame descriptor pointer to a1 (deoptimizer->input_); 198 // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below. 199 __ mov(a0, v0); 200 __ ld(a1, MemOperand(v0, Deoptimizer::input_offset())); 201 202 // Copy core registers into FrameDescription::registers_[kNumRegisters]. 203 DCHECK(Register::kNumRegisters == kNumberOfRegisters); 204 for (int i = 0; i < kNumberOfRegisters; i++) { 205 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 206 if ((saved_regs & (1 << i)) != 0) { 207 __ ld(a2, MemOperand(sp, i * kPointerSize)); 208 __ sd(a2, MemOperand(a1, offset)); 209 } else if (FLAG_debug_code) { 210 __ li(a2, kDebugZapValue); 211 __ sd(a2, MemOperand(a1, offset)); 212 } 213 } 214 215 int double_regs_offset = FrameDescription::double_registers_offset(); 216 // Copy FPU registers to 217 // double_registers_[DoubleRegister::kNumAllocatableRegisters] 218 for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++i) { 219 int dst_offset = i * kDoubleSize + double_regs_offset; 220 int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize; 221 __ ldc1(f0, MemOperand(sp, src_offset)); 222 __ sdc1(f0, MemOperand(a1, dst_offset)); 223 } 224 225 // Remove the bailout id and the saved registers from the stack. 226 __ Daddu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); 227 228 // Compute a pointer to the unwinding limit in register a2; that is 229 // the first stack slot not part of the input frame. 230 __ ld(a2, MemOperand(a1, FrameDescription::frame_size_offset())); 231 __ Daddu(a2, a2, sp); 232 233 // Unwind the stack down to - but not including - the unwinding 234 // limit and copy the contents of the activation frame to the input 235 // frame description. 236 __ Daddu(a3, a1, Operand(FrameDescription::frame_content_offset())); 237 Label pop_loop; 238 Label pop_loop_header; 239 __ BranchShort(&pop_loop_header); 240 __ bind(&pop_loop); 241 __ pop(a4); 242 __ sd(a4, MemOperand(a3, 0)); 243 __ daddiu(a3, a3, sizeof(uint64_t)); 244 __ bind(&pop_loop_header); 245 __ BranchShort(&pop_loop, ne, a2, Operand(sp)); 246 // Compute the output frame in the deoptimizer. 247 __ push(a0); // Preserve deoptimizer object across call. 248 // a0: deoptimizer object; a1: scratch. 249 __ PrepareCallCFunction(1, a1); 250 // Call Deoptimizer::ComputeOutputFrames(). 251 { 252 AllowExternalCallThatCantCauseGC scope(masm()); 253 __ CallCFunction( 254 ExternalReference::compute_output_frames_function(isolate()), 1); 255 } 256 __ pop(a0); // Restore deoptimizer object (class Deoptimizer). 257 258 // Replace the current (input) frame with the output frames. 259 Label outer_push_loop, inner_push_loop, 260 outer_loop_header, inner_loop_header; 261 // Outer loop state: a4 = current "FrameDescription** output_", 262 // a1 = one past the last FrameDescription**. 263 __ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset())); 264 __ ld(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_. 265 __ dsll(a1, a1, kPointerSizeLog2); // Count to offset. 266 __ daddu(a1, a4, a1); // a1 = one past the last FrameDescription**. 267 __ jmp(&outer_loop_header); 268 __ bind(&outer_push_loop); 269 // Inner loop state: a2 = current FrameDescription*, a3 = loop index. 270 __ ld(a2, MemOperand(a4, 0)); // output_[ix] 271 __ ld(a3, MemOperand(a2, FrameDescription::frame_size_offset())); 272 __ jmp(&inner_loop_header); 273 __ bind(&inner_push_loop); 274 __ Dsubu(a3, a3, Operand(sizeof(uint64_t))); 275 __ Daddu(a6, a2, Operand(a3)); 276 __ ld(a7, MemOperand(a6, FrameDescription::frame_content_offset())); 277 __ push(a7); 278 __ bind(&inner_loop_header); 279 __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg)); 280 281 __ Daddu(a4, a4, Operand(kPointerSize)); 282 __ bind(&outer_loop_header); 283 __ BranchShort(&outer_push_loop, lt, a4, Operand(a1)); 284 285 __ ld(a1, MemOperand(a0, Deoptimizer::input_offset())); 286 for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) { 287 const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i); 288 int src_offset = i * kDoubleSize + double_regs_offset; 289 __ ldc1(fpu_reg, MemOperand(a1, src_offset)); 290 } 291 292 // Push state, pc, and continuation from the last output frame. 293 __ ld(a6, MemOperand(a2, FrameDescription::state_offset())); 294 __ push(a6); 295 296 __ ld(a6, MemOperand(a2, FrameDescription::pc_offset())); 297 __ push(a6); 298 __ ld(a6, MemOperand(a2, FrameDescription::continuation_offset())); 299 __ push(a6); 300 301 302 // Technically restoring 'at' should work unless zero_reg is also restored 303 // but it's safer to check for this. 304 DCHECK(!(at.bit() & restored_regs)); 305 // Restore the registers from the last output frame. 306 __ mov(at, a2); 307 for (int i = kNumberOfRegisters - 1; i >= 0; i--) { 308 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 309 if ((restored_regs & (1 << i)) != 0) { 310 __ ld(ToRegister(i), MemOperand(at, offset)); 311 } 312 } 313 314 __ InitializeRootRegister(); 315 316 __ pop(at); // Get continuation, leave pc on stack. 317 __ pop(ra); 318 __ Jump(at); 319 __ stop("Unreachable."); 320} 321 322 323// Maximum size of a table entry generated below. 324const int Deoptimizer::table_entry_size_ = 11 * Assembler::kInstrSize; 325 326void Deoptimizer::TableEntryGenerator::GeneratePrologue() { 327 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm()); 328 329 // Create a sequence of deoptimization entries. 330 // Note that registers are still live when jumping to an entry. 331 Label table_start; 332 __ bind(&table_start); 333 for (int i = 0; i < count(); i++) { 334 Label start; 335 __ bind(&start); 336 __ daddiu(sp, sp, -1 * kPointerSize); 337 // Jump over the remaining deopt entries (including this one). 338 // This code is always reached by calling Jump, which puts the target (label 339 // start) into t9. 340 const int remaining_entries = (count() - i) * table_entry_size_; 341 __ Daddu(t9, t9, remaining_entries); 342 // 'at' was clobbered so we can only load the current entry value here. 343 __ li(t8, i); 344 __ jr(t9); // Expose delay slot. 345 __ sd(t8, MemOperand(sp, 0 * kPointerSize)); // In the delay slot. 346 347 // Pad the rest of the code. 348 while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) { 349 __ nop(); 350 } 351 352 DCHECK_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start)); 353 } 354 355 DCHECK_EQ(masm()->SizeOfCodeGeneratedSince(&table_start), 356 count() * table_entry_size_); 357} 358 359 360void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { 361 SetFrameSlot(offset, value); 362} 363 364 365void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { 366 SetFrameSlot(offset, value); 367} 368 369 370void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { 371 // No out-of-line constant pool support. 372 UNREACHABLE(); 373} 374 375 376#undef __ 377 378 379} } // namespace v8::internal 380