| OLD | NEW | 
|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "src/v8.h" | 5 #include "src/v8.h" | 
| 6 | 6 | 
| 7 #if V8_TARGET_ARCH_X87 | 7 #if V8_TARGET_ARCH_X87 | 
| 8 | 8 | 
| 9 #include "src/codegen.h" | 9 #include "src/codegen.h" | 
| 10 #include "src/debug.h" | 10 #include "src/debug.h" | 
| 11 | 11 | 
| 12 | 12 | 
| 13 namespace v8 { | 13 namespace v8 { | 
| 14 namespace internal { | 14 namespace internal { | 
| 15 | 15 | 
| 16 // Patch the code at the current PC with a call to the target address. | 16 #define __ ACCESS_MASM(masm) | 
| 17 // Additional guard int3 instructions can be added if required. |  | 
| 18 void PatchCodeWithCall(Address pc, Address target, int guard_bytes) { |  | 
| 19   // Call instruction takes up 5 bytes and int3 takes up one byte. |  | 
| 20   static const int kCallCodeSize = 5; |  | 
| 21   int code_size = kCallCodeSize + guard_bytes; |  | 
| 22 | 17 | 
| 23   // Create a code patcher. |  | 
| 24   CodePatcher patcher(pc, code_size); |  | 
| 25 | 18 | 
| 26 // Add a label for checking the size of the code used for returning. | 19 void EmitDebugBreakSlot(MacroAssembler* masm) { | 
| 27 #ifdef DEBUG |  | 
| 28   Label check_codesize; | 20   Label check_codesize; | 
| 29   patcher.masm()->bind(&check_codesize); | 21   __ bind(&check_codesize); | 
| 30 #endif | 22   __ Nop(Assembler::kDebugBreakSlotLength); | 
| 31 | 23   DCHECK_EQ(Assembler::kDebugBreakSlotLength, | 
| 32   // Patch the code. | 24             masm->SizeOfCodeGeneratedSince(&check_codesize)); | 
| 33   patcher.masm()->call(target, RelocInfo::NONE32); |  | 
| 34 |  | 
| 35   // Check that the size of the code generated is as expected. |  | 
| 36   DCHECK_EQ(kCallCodeSize, |  | 
| 37             patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize)); |  | 
| 38 |  | 
| 39   // Add the requested number of int3 instructions after the call. |  | 
| 40   DCHECK_GE(guard_bytes, 0); |  | 
| 41   for (int i = 0; i < guard_bytes; i++) { |  | 
| 42     patcher.masm()->int3(); |  | 
| 43   } |  | 
| 44 |  | 
| 45   CpuFeatures::FlushICache(pc, code_size); |  | 
| 46 } | 25 } | 
| 47 | 26 | 
| 48 | 27 | 
| 49 // Patch the JS frame exit code with a debug break call. See | 28 void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode, | 
| 50 // CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x87.cc | 29                                 int call_argc) { | 
| 51 // for the precise return instructions sequence. | 30   // Generate enough nop's to make space for a call instruction. | 
| 52 void BreakLocation::SetDebugBreakAtReturn() { | 31   masm->RecordDebugBreakSlot(mode, call_argc); | 
| 53   DCHECK(Assembler::kJSReturnSequenceLength >= | 32   EmitDebugBreakSlot(masm); | 
| 54          Assembler::kCallInstructionLength); |  | 
| 55   PatchCodeWithCall( |  | 
| 56       pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(), |  | 
| 57       Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength); |  | 
| 58 } | 33 } | 
| 59 | 34 | 
| 60 | 35 | 
| 61 void BreakLocation::SetDebugBreakAtSlot() { | 36 void DebugCodegen::ClearDebugBreakSlot(Address pc) { | 
| 62   DCHECK(IsDebugBreakSlot()); | 37   CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength); | 
| 63   Isolate* isolate = debug_info_->GetIsolate(); | 38   EmitDebugBreakSlot(patcher.masm()); | 
| 64   PatchCodeWithCall( |  | 
| 65       pc(), isolate->builtins()->Slot_DebugBreak()->entry(), |  | 
| 66       Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength); |  | 
| 67 } | 39 } | 
| 68 | 40 | 
| 69 | 41 | 
| 70 #define __ ACCESS_MASM(masm) | 42 void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) { | 
|  | 43   DCHECK_EQ(Code::BUILTIN, code->kind()); | 
|  | 44   static const int kSize = Assembler::kDebugBreakSlotLength; | 
|  | 45   CodePatcher patcher(pc, kSize); | 
| 71 | 46 | 
| 72 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, | 47   // Add a label for checking the size of the code used for returning. | 
| 73                                           RegList object_regs) { | 48   Label check_codesize; | 
|  | 49   patcher.masm()->bind(&check_codesize); | 
|  | 50   patcher.masm()->call(code->entry(), RelocInfo::NONE32); | 
|  | 51   // Check that the size of the code generated is as expected. | 
|  | 52   DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize)); | 
|  | 53 } | 
|  | 54 | 
|  | 55 | 
|  | 56 void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, | 
|  | 57                                           DebugBreakCallHelperMode mode) { | 
|  | 58   __ RecordComment("Debug break"); | 
|  | 59 | 
| 74   // Enter an internal frame. | 60   // Enter an internal frame. | 
| 75   { | 61   { | 
| 76     FrameScope scope(masm, StackFrame::INTERNAL); | 62     FrameScope scope(masm, StackFrame::INTERNAL); | 
| 77 | 63 | 
| 78     // Load padding words on stack. | 64     // Load padding words on stack. | 
| 79     for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) { | 65     for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) { | 
| 80       __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue))); | 66       __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue))); | 
| 81     } | 67     } | 
| 82     __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize))); | 68     __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize))); | 
| 83 | 69 | 
| 84     // Store the registers containing live values on the expression stack to | 70     if (mode == SAVE_RESULT_REGISTER) __ push(eax); | 
| 85     // make sure that these are correctly updated during GC. Non object values |  | 
| 86     // are stored as a smi causing it to be untouched by GC. |  | 
| 87     DCHECK((object_regs & ~kJSCallerSaved) == 0); |  | 
| 88     for (int i = 0; i < kNumJSCallerSaved; i++) { |  | 
| 89       int r = JSCallerSavedCode(i); |  | 
| 90       Register reg = { r }; |  | 
| 91       if ((object_regs & (1 << r)) != 0) { |  | 
| 92         __ push(reg); |  | 
| 93       } |  | 
| 94     } |  | 
| 95 | 71 | 
| 96 #ifdef DEBUG |  | 
| 97     __ RecordComment("// Calling from debug break to runtime - come in - over"); |  | 
| 98 #endif |  | 
| 99     __ Move(eax, Immediate(0));  // No arguments. | 72     __ Move(eax, Immediate(0));  // No arguments. | 
| 100     __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate()))); | 73     __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate()))); | 
| 101 | 74 | 
| 102     CEntryStub ceb(masm->isolate(), 1); | 75     CEntryStub ceb(masm->isolate(), 1); | 
| 103     __ CallStub(&ceb); | 76     __ CallStub(&ceb); | 
| 104 | 77 | 
| 105     // Automatically find register that could be used after register restore. | 78     if (FLAG_debug_code) { | 
| 106     // We need one register for padding skip instructions. | 79       for (int i = 0; i < kNumJSCallerSaved; ++i) { | 
| 107     Register unused_reg = { -1 }; | 80         Register reg = {JSCallerSavedCode(i)}; | 
| 108 |  | 
| 109     // Restore the register values containing object pointers from the |  | 
| 110     // expression stack. |  | 
| 111     for (int i = kNumJSCallerSaved; --i >= 0;) { |  | 
| 112       int r = JSCallerSavedCode(i); |  | 
| 113       Register reg = { r }; |  | 
| 114       if (FLAG_debug_code) { |  | 
| 115         __ Move(reg, Immediate(kDebugZapValue)); | 81         __ Move(reg, Immediate(kDebugZapValue)); | 
| 116       } | 82       } | 
| 117       bool taken = reg.code() == esi.code(); |  | 
| 118       if ((object_regs & (1 << r)) != 0) { |  | 
| 119         __ pop(reg); |  | 
| 120         taken = true; |  | 
| 121       } |  | 
| 122       if (!taken) { |  | 
| 123         unused_reg = reg; |  | 
| 124       } |  | 
| 125     } | 83     } | 
| 126 | 84 | 
| 127     DCHECK(unused_reg.code() != -1); | 85     if (mode == SAVE_RESULT_REGISTER) __ pop(eax); | 
| 128 | 86 | 
| 129     // Read current padding counter and skip corresponding number of words. | 87     __ pop(ebx); | 
| 130     __ pop(unused_reg); |  | 
| 131     // We divide stored value by 2 (untagging) and multiply it by word's size. | 88     // We divide stored value by 2 (untagging) and multiply it by word's size. | 
| 132     STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0); | 89     STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0); | 
| 133     __ lea(esp, Operand(esp, unused_reg, times_half_pointer_size, 0)); | 90     __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0)); | 
| 134 | 91 | 
| 135     // Get rid of the internal frame. | 92     // Get rid of the internal frame. | 
| 136   } | 93   } | 
| 137 | 94 | 
| 138   // This call did not replace a call , so there will be an unwanted | 95   // This call did not replace a call , so there will be an unwanted | 
| 139   // return address left on the stack. Here we get rid of that. | 96   // return address left on the stack. Here we get rid of that. | 
| 140   __ add(esp, Immediate(kPointerSize)); | 97   __ add(esp, Immediate(kPointerSize)); | 
| 141 | 98 | 
| 142   // Now that the break point has been handled, resume normal execution by | 99   // Now that the break point has been handled, resume normal execution by | 
| 143   // jumping to the target address intended by the caller and that was | 100   // jumping to the target address intended by the caller and that was | 
| 144   // overwritten by the address of DebugBreakXXX. | 101   // overwritten by the address of DebugBreakXXX. | 
| 145   ExternalReference after_break_target = | 102   ExternalReference after_break_target = | 
| 146       ExternalReference::debug_after_break_target_address(masm->isolate()); | 103       ExternalReference::debug_after_break_target_address(masm->isolate()); | 
| 147   __ jmp(Operand::StaticVariable(after_break_target)); | 104   __ jmp(Operand::StaticVariable(after_break_target)); | 
| 148 } | 105 } | 
| 149 | 106 | 
| 150 | 107 | 
| 151 void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) { |  | 
| 152   // Register state just before return from JS function (from codegen-x87.cc). |  | 
| 153   // ----------- S t a t e ------------- |  | 
| 154   //  -- eax: return value |  | 
| 155   // ----------------------------------- |  | 
| 156   Generate_DebugBreakCallHelper(masm, eax.bit()); |  | 
| 157 } |  | 
| 158 |  | 
| 159 |  | 
| 160 void DebugCodegen::GenerateSlot(MacroAssembler* masm, |  | 
| 161                                 DebugCodegen::SlotLocation location, |  | 
| 162                                 int call_argc) { |  | 
| 163   // Generate enough nop's to make space for a call instruction. |  | 
| 164   Label check_codesize; |  | 
| 165   __ bind(&check_codesize); |  | 
| 166   RecordRelocInfo(masm, location, call_argc); |  | 
| 167   __ Nop(Assembler::kDebugBreakSlotLength); |  | 
| 168   DCHECK_EQ(Assembler::kDebugBreakSlotLength, |  | 
| 169             masm->SizeOfCodeGeneratedSince(&check_codesize)); |  | 
| 170 } |  | 
| 171 |  | 
| 172 |  | 
| 173 void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) { |  | 
| 174   Generate_DebugBreakCallHelper(masm, 0); |  | 
| 175 } |  | 
| 176 |  | 
| 177 |  | 
| 178 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { | 108 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { | 
| 179   masm->ret(0); | 109   masm->ret(0); | 
| 180 } | 110 } | 
| 181 | 111 | 
| 182 | 112 | 
| 183 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { | 113 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { | 
| 184   ExternalReference restarter_frame_function_slot = | 114   ExternalReference restarter_frame_function_slot = | 
| 185       ExternalReference::debug_restarter_frame_function_pointer_address( | 115       ExternalReference::debug_restarter_frame_function_pointer_address( | 
| 186           masm->isolate()); | 116           masm->isolate()); | 
| 187   __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0)); | 117   __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0)); | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 206 | 136 | 
| 207 | 137 | 
| 208 const bool LiveEdit::kFrameDropperSupported = true; | 138 const bool LiveEdit::kFrameDropperSupported = true; | 
| 209 | 139 | 
| 210 #undef __ | 140 #undef __ | 
| 211 | 141 | 
| 212 }  // namespace internal | 142 }  // namespace internal | 
| 213 }  // namespace v8 | 143 }  // namespace v8 | 
| 214 | 144 | 
| 215 #endif  // V8_TARGET_ARCH_X87 | 145 #endif  // V8_TARGET_ARCH_X87 | 
| OLD | NEW | 
|---|