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/assembler.h" 8 #include "src/codegen.h" 9 #include "src/debug/debug.h" 10 11 12 namespace v8 { 13 namespace internal { 14 15 #define __ ACCESS_MASM(masm) 16 17 18 void EmitDebugBreakSlot(MacroAssembler* masm) { 19 Label check_codesize; 20 __ bind(&check_codesize); 21 __ Nop(Assembler::kDebugBreakSlotLength); 22 DCHECK_EQ(Assembler::kDebugBreakSlotLength, 23 masm->SizeOfCodeGeneratedSince(&check_codesize)); 24 } 25 26 27 void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) { 28 // Generate enough nop's to make space for a call instruction. 29 masm->RecordDebugBreakSlot(mode); 30 EmitDebugBreakSlot(masm); 31 } 32 33 34 void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) { 35 CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength); 36 EmitDebugBreakSlot(patcher.masm()); 37 } 38 39 40 void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc, 41 Handle<Code> code) { 42 DCHECK(code->is_debug_stub()); 43 static const int kSize = Assembler::kDebugBreakSlotLength; 44 CodePatcher patcher(isolate, pc, kSize); 45 Label check_codesize; 46 patcher.masm()->bind(&check_codesize); 47 patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(code->entry()), 48 Assembler::RelocInfoNone()); 49 patcher.masm()->call(kScratchRegister); 50 // Check that the size of the code generated is as expected. 51 DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize)); 52 } 53 54 bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) { 55 return !Assembler::IsNop(pc); 56 } 57 58 void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, 59 DebugBreakCallHelperMode mode) { 60 __ RecordComment("Debug break"); 61 62 // Enter an internal frame. 63 { 64 FrameScope scope(masm, StackFrame::INTERNAL); 65 66 // Load padding words on stack. 67 for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) { 68 __ Push(Smi::FromInt(LiveEdit::kFramePaddingValue)); 69 } 70 __ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)); 71 72 // Push arguments for DebugBreak call. 73 if (mode == SAVE_RESULT_REGISTER) { 74 // Break on return. 75 __ Push(rax); 76 } else { 77 // Non-return breaks. 78 __ Push(masm->isolate()->factory()->the_hole_value()); 79 } 80 __ Set(rax, 1); 81 __ Move(rbx, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak), 82 masm->isolate())); 83 84 CEntryStub ceb(masm->isolate(), 1); 85 __ CallStub(&ceb); 86 87 if (FLAG_debug_code) { 88 for (int i = 0; i < kNumJSCallerSaved; ++i) { 89 Register reg = {JSCallerSavedCode(i)}; 90 // Do not clobber rax if mode is SAVE_RESULT_REGISTER. It will 91 // contain return value of the function. 92 if (!(reg.is(rax) && (mode == SAVE_RESULT_REGISTER))) { 93 __ Set(reg, kDebugZapValue); 94 } 95 } 96 } 97 98 // Read current padding counter and skip corresponding number of words. 99 __ Pop(kScratchRegister); 100 __ SmiToInteger32(kScratchRegister, kScratchRegister); 101 __ leap(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0)); 102 103 // Get rid of the internal frame. 104 } 105 106 // This call did not replace a call , so there will be an unwanted 107 // return address left on the stack. Here we get rid of that. 108 __ addp(rsp, Immediate(kPCOnStackSize)); 109 110 // Now that the break point has been handled, resume normal execution by 111 // jumping to the target address intended by the caller and that was 112 // overwritten by the address of DebugBreakXXX. 113 ExternalReference after_break_target = 114 ExternalReference::debug_after_break_target_address(masm->isolate()); 115 __ Move(kScratchRegister, after_break_target); 116 __ Jump(Operand(kScratchRegister, 0)); 117 } 118 119 120 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 121 // We do not know our frame height, but set rsp based on rbp. 122 __ leap(rsp, Operand(rbp, FrameDropperFrameConstants::kFunctionOffset)); 123 __ Pop(rdi); // Function. 124 __ addp(rsp, 125 Immediate(-FrameDropperFrameConstants::kCodeOffset)); // INTERNAL 126 // frame marker 127 // and code 128 __ popq(rbp); 129 130 ParameterCount dummy(0); 131 __ FloodFunctionIfStepping(rdi, no_reg, dummy, dummy); 132 133 // Load context from the function. 134 __ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); 135 136 // Clear new.target as a safety measure. 137 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); 138 139 // Get function code. 140 __ movp(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); 141 __ movp(rbx, FieldOperand(rbx, SharedFunctionInfo::kCodeOffset)); 142 __ leap(rbx, FieldOperand(rbx, Code::kHeaderSize)); 143 144 // Re-run JSFunction, rdi is function, rsi is context. 145 __ jmp(rbx); 146 } 147 148 const bool LiveEdit::kFrameDropperSupported = true; 149 150 #undef __ 151 152 } // namespace internal 153 } // namespace v8 154 155 #endif // V8_TARGET_ARCH_X64 156