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