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