1 // Copyright 2012 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 #if V8_TARGET_ARCH_X64 8 9 #include "src/codegen.h" 10 #include "src/deoptimizer.h" 11 #include "src/full-codegen.h" 12 #include "src/safepoint-table.h" 13 14 namespace v8 { 15 namespace internal { 16 17 18 const int Deoptimizer::table_entry_size_ = 10; 19 20 21 int Deoptimizer::patch_size() { 22 return Assembler::kCallSequenceLength; 23 } 24 25 26 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { 27 // Invalidate the relocation information, as it will become invalid by the 28 // code patching below, and is not needed any more. 29 code->InvalidateRelocation(); 30 31 if (FLAG_zap_code_space) { 32 // Fail hard and early if we enter this code object again. 33 byte* pointer = code->FindCodeAgeSequence(); 34 if (pointer != NULL) { 35 pointer += kNoCodeAgeSequenceLength; 36 } else { 37 pointer = code->instruction_start(); 38 } 39 CodePatcher patcher(pointer, 1); 40 patcher.masm()->int3(); 41 42 DeoptimizationInputData* data = 43 DeoptimizationInputData::cast(code->deoptimization_data()); 44 int osr_offset = data->OsrPcOffset()->value(); 45 if (osr_offset > 0) { 46 CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1); 47 osr_patcher.masm()->int3(); 48 } 49 } 50 51 // For each LLazyBailout instruction insert a absolute call to the 52 // corresponding deoptimization entry, or a short call to an absolute 53 // jump if space is short. The absolute jumps are put in a table just 54 // before the safepoint table (space was allocated there when the Code 55 // object was created, if necessary). 56 57 Address instruction_start = code->instruction_start(); 58 #ifdef DEBUG 59 Address prev_call_address = NULL; 60 #endif 61 DeoptimizationInputData* deopt_data = 62 DeoptimizationInputData::cast(code->deoptimization_data()); 63 SharedFunctionInfo* shared = 64 SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo()); 65 shared->EvictFromOptimizedCodeMap(code, "deoptimized code"); 66 deopt_data->SetSharedFunctionInfo(Smi::FromInt(0)); 67 // For each LLazyBailout instruction insert a call to the corresponding 68 // deoptimization entry. 69 for (int i = 0; i < deopt_data->DeoptCount(); i++) { 70 if (deopt_data->Pc(i)->value() == -1) continue; 71 // Position where Call will be patched in. 72 Address call_address = instruction_start + deopt_data->Pc(i)->value(); 73 // There is room enough to write a long call instruction because we pad 74 // LLazyBailout instructions with nops if necessary. 75 CodePatcher patcher(call_address, Assembler::kCallSequenceLength); 76 patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY), 77 Assembler::RelocInfoNone()); 78 ASSERT(prev_call_address == NULL || 79 call_address >= prev_call_address + patch_size()); 80 ASSERT(call_address + patch_size() <= code->instruction_end()); 81 #ifdef DEBUG 82 prev_call_address = call_address; 83 #endif 84 } 85 } 86 87 88 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { 89 // Set the register values. The values are not important as there are no 90 // callee saved registers in JavaScript frames, so all registers are 91 // spilled. Registers rbp and rsp are set to the correct values though. 92 for (int i = 0; i < Register::kNumRegisters; i++) { 93 input_->SetRegister(i, i * 4); 94 } 95 input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp())); 96 input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp())); 97 for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) { 98 input_->SetDoubleRegister(i, 0.0); 99 } 100 101 // Fill the frame content from the actual data on the frame. 102 for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { 103 input_->SetFrameSlot(i, Memory::uintptr_at(tos + i)); 104 } 105 } 106 107 108 void Deoptimizer::SetPlatformCompiledStubRegisters( 109 FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) { 110 intptr_t handler = 111 reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_); 112 int params = descriptor->GetHandlerParameterCount(); 113 output_frame->SetRegister(rax.code(), params); 114 output_frame->SetRegister(rbx.code(), handler); 115 } 116 117 118 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { 119 for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) { 120 double double_value = input_->GetDoubleRegister(i); 121 output_frame->SetDoubleRegister(i, double_value); 122 } 123 } 124 125 126 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) { 127 // There is no dynamic alignment padding on x64 in the input frame. 128 return false; 129 } 130 131 132 #define __ masm()-> 133 134 void Deoptimizer::EntryGenerator::Generate() { 135 GeneratePrologue(); 136 137 // Save all general purpose registers before messing with them. 138 const int kNumberOfRegisters = Register::kNumRegisters; 139 140 const int kDoubleRegsSize = kDoubleSize * 141 XMMRegister::NumAllocatableRegisters(); 142 __ subp(rsp, Immediate(kDoubleRegsSize)); 143 144 for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) { 145 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i); 146 int offset = i * kDoubleSize; 147 __ movsd(Operand(rsp, offset), xmm_reg); 148 } 149 150 // We push all registers onto the stack, even though we do not need 151 // to restore all later. 152 for (int i = 0; i < kNumberOfRegisters; i++) { 153 Register r = Register::from_code(i); 154 __ pushq(r); 155 } 156 157 const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize + 158 kDoubleRegsSize; 159 160 // We use this to keep the value of the fifth argument temporarily. 161 // Unfortunately we can't store it directly in r8 (used for passing 162 // this on linux), since it is another parameter passing register on windows. 163 Register arg5 = r11; 164 165 // Get the bailout id from the stack. 166 __ movp(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize)); 167 168 // Get the address of the location in the code object 169 // and compute the fp-to-sp delta in register arg5. 170 __ movp(arg_reg_4, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize)); 171 __ leap(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kRegisterSize + 172 kPCOnStackSize)); 173 174 __ subp(arg5, rbp); 175 __ negp(arg5); 176 177 // Allocate a new deoptimizer object. 178 __ PrepareCallCFunction(6); 179 __ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 180 __ movp(arg_reg_1, rax); 181 __ Set(arg_reg_2, type()); 182 // Args 3 and 4 are already in the right registers. 183 184 // On windows put the arguments on the stack (PrepareCallCFunction 185 // has created space for this). On linux pass the arguments in r8 and r9. 186 #ifdef _WIN64 187 __ movq(Operand(rsp, 4 * kRegisterSize), arg5); 188 __ LoadAddress(arg5, ExternalReference::isolate_address(isolate())); 189 __ movq(Operand(rsp, 5 * kRegisterSize), arg5); 190 #else 191 __ movp(r8, arg5); 192 __ LoadAddress(r9, ExternalReference::isolate_address(isolate())); 193 #endif 194 195 { AllowExternalCallThatCantCauseGC scope(masm()); 196 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); 197 } 198 // Preserve deoptimizer object in register rax and get the input 199 // frame descriptor pointer. 200 __ movp(rbx, Operand(rax, Deoptimizer::input_offset())); 201 202 // Fill in the input registers. 203 for (int i = kNumberOfRegisters -1; i >= 0; i--) { 204 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 205 __ PopQuad(Operand(rbx, offset)); 206 } 207 208 // Fill in the double input registers. 209 int double_regs_offset = FrameDescription::double_registers_offset(); 210 for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) { 211 int dst_offset = i * kDoubleSize + double_regs_offset; 212 __ popq(Operand(rbx, dst_offset)); 213 } 214 215 // Remove the bailout id and return address from the stack. 216 __ addp(rsp, Immediate(1 * kRegisterSize + kPCOnStackSize)); 217 218 // Compute a pointer to the unwinding limit in register rcx; that is 219 // the first stack slot not part of the input frame. 220 __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 221 __ addp(rcx, rsp); 222 223 // Unwind the stack down to - but not including - the unwinding 224 // limit and copy the contents of the activation frame to the input 225 // frame description. 226 __ leap(rdx, Operand(rbx, FrameDescription::frame_content_offset())); 227 Label pop_loop_header; 228 __ jmp(&pop_loop_header); 229 Label pop_loop; 230 __ bind(&pop_loop); 231 __ Pop(Operand(rdx, 0)); 232 __ addp(rdx, Immediate(sizeof(intptr_t))); 233 __ bind(&pop_loop_header); 234 __ cmpp(rcx, rsp); 235 __ j(not_equal, &pop_loop); 236 237 // Compute the output frame in the deoptimizer. 238 __ pushq(rax); 239 __ PrepareCallCFunction(2); 240 __ movp(arg_reg_1, rax); 241 __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate())); 242 { 243 AllowExternalCallThatCantCauseGC scope(masm()); 244 __ CallCFunction( 245 ExternalReference::compute_output_frames_function(isolate()), 2); 246 } 247 __ popq(rax); 248 249 // Replace the current frame with the output frames. 250 Label outer_push_loop, inner_push_loop, 251 outer_loop_header, inner_loop_header; 252 // Outer loop state: rax = current FrameDescription**, rdx = one past the 253 // last FrameDescription**. 254 __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset())); 255 __ movp(rax, Operand(rax, Deoptimizer::output_offset())); 256 __ leap(rdx, Operand(rax, rdx, times_pointer_size, 0)); 257 __ jmp(&outer_loop_header); 258 __ bind(&outer_push_loop); 259 // Inner loop state: rbx = current FrameDescription*, rcx = loop index. 260 __ movp(rbx, Operand(rax, 0)); 261 __ movp(rcx, Operand(rbx, FrameDescription::frame_size_offset())); 262 __ jmp(&inner_loop_header); 263 __ bind(&inner_push_loop); 264 __ subp(rcx, Immediate(sizeof(intptr_t))); 265 __ Push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset())); 266 __ bind(&inner_loop_header); 267 __ testp(rcx, rcx); 268 __ j(not_zero, &inner_push_loop); 269 __ addp(rax, Immediate(kPointerSize)); 270 __ bind(&outer_loop_header); 271 __ cmpp(rax, rdx); 272 __ j(below, &outer_push_loop); 273 274 for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) { 275 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i); 276 int src_offset = i * kDoubleSize + double_regs_offset; 277 __ movsd(xmm_reg, Operand(rbx, src_offset)); 278 } 279 280 // Push state, pc, and continuation from the last output frame. 281 __ Push(Operand(rbx, FrameDescription::state_offset())); 282 __ PushQuad(Operand(rbx, FrameDescription::pc_offset())); 283 __ PushQuad(Operand(rbx, FrameDescription::continuation_offset())); 284 285 // Push the registers from the last output frame. 286 for (int i = 0; i < kNumberOfRegisters; i++) { 287 int offset = (i * kPointerSize) + FrameDescription::registers_offset(); 288 __ PushQuad(Operand(rbx, offset)); 289 } 290 291 // Restore the registers from the stack. 292 for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) { 293 Register r = Register::from_code(i); 294 // Do not restore rsp, simply pop the value into the next register 295 // and overwrite this afterwards. 296 if (r.is(rsp)) { 297 ASSERT(i > 0); 298 r = Register::from_code(i - 1); 299 } 300 __ popq(r); 301 } 302 303 // Set up the roots register. 304 __ InitializeRootRegister(); 305 __ InitializeSmiConstantRegister(); 306 307 // Return to the continuation point. 308 __ ret(0); 309 } 310 311 312 void Deoptimizer::TableEntryGenerator::GeneratePrologue() { 313 // Create a sequence of deoptimization entries. 314 Label done; 315 for (int i = 0; i < count(); i++) { 316 int start = masm()->pc_offset(); 317 USE(start); 318 __ pushq_imm32(i); 319 __ jmp(&done); 320 ASSERT(masm()->pc_offset() - start == table_entry_size_); 321 } 322 __ bind(&done); 323 } 324 325 326 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { 327 if (kPCOnStackSize == 2 * kPointerSize) { 328 // Zero out the high-32 bit of PC for x32 port. 329 SetFrameSlot(offset + kPointerSize, 0); 330 } 331 SetFrameSlot(offset, value); 332 } 333 334 335 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { 336 if (kFPOnStackSize == 2 * kPointerSize) { 337 // Zero out the high-32 bit of FP for x32 port. 338 SetFrameSlot(offset + kPointerSize, 0); 339 } 340 SetFrameSlot(offset, value); 341 } 342 343 344 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { 345 // No out-of-line constant pool support. 346 UNREACHABLE(); 347 } 348 349 350 #undef __ 351 352 353 } } // namespace v8::internal 354 355 #endif // V8_TARGET_ARCH_X64 356