1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #if V8_TARGET_ARCH_IA32 31 32 #include "codegen.h" 33 #include "debug.h" 34 35 36 namespace v8 { 37 namespace internal { 38 39 #ifdef ENABLE_DEBUGGER_SUPPORT 40 41 bool BreakLocationIterator::IsDebugBreakAtReturn() { 42 return Debug::IsDebugBreakAtReturn(rinfo()); 43 } 44 45 46 // Patch the JS frame exit code with a debug break call. See 47 // CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc 48 // for the precise return instructions sequence. 49 void BreakLocationIterator::SetDebugBreakAtReturn() { 50 ASSERT(Assembler::kJSReturnSequenceLength >= 51 Assembler::kCallInstructionLength); 52 Isolate* isolate = Isolate::Current(); 53 rinfo()->PatchCodeWithCall(isolate->debug()->debug_break_return()->entry(), 54 Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength); 55 } 56 57 58 // Restore the JS frame exit code. 59 void BreakLocationIterator::ClearDebugBreakAtReturn() { 60 rinfo()->PatchCode(original_rinfo()->pc(), 61 Assembler::kJSReturnSequenceLength); 62 } 63 64 65 // A debug break in the frame exit code is identified by the JS frame exit code 66 // having been patched with a call instruction. 67 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { 68 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); 69 return rinfo->IsPatchedReturnSequence(); 70 } 71 72 73 bool BreakLocationIterator::IsDebugBreakAtSlot() { 74 ASSERT(IsDebugBreakSlot()); 75 // Check whether the debug break slot instructions have been patched. 76 return rinfo()->IsPatchedDebugBreakSlotSequence(); 77 } 78 79 80 void BreakLocationIterator::SetDebugBreakAtSlot() { 81 ASSERT(IsDebugBreakSlot()); 82 Isolate* isolate = Isolate::Current(); 83 rinfo()->PatchCodeWithCall( 84 isolate->debug()->debug_break_slot()->entry(), 85 Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength); 86 } 87 88 89 void BreakLocationIterator::ClearDebugBreakAtSlot() { 90 ASSERT(IsDebugBreakSlot()); 91 rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength); 92 } 93 94 95 // All debug break stubs support padding for LiveEdit. 96 const bool Debug::FramePaddingLayout::kIsSupported = true; 97 98 99 #define __ ACCESS_MASM(masm) 100 101 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, 102 RegList object_regs, 103 RegList non_object_regs, 104 bool convert_call_to_jmp) { 105 // Enter an internal frame. 106 { 107 FrameScope scope(masm, StackFrame::INTERNAL); 108 109 // Load padding words on stack. 110 for (int i = 0; i < Debug::FramePaddingLayout::kInitialSize; i++) { 111 __ push(Immediate(Smi::FromInt( 112 Debug::FramePaddingLayout::kPaddingValue))); 113 } 114 __ push(Immediate(Smi::FromInt(Debug::FramePaddingLayout::kInitialSize))); 115 116 // Store the registers containing live values on the expression stack to 117 // make sure that these are correctly updated during GC. Non object values 118 // are stored as a smi causing it to be untouched by GC. 119 ASSERT((object_regs & ~kJSCallerSaved) == 0); 120 ASSERT((non_object_regs & ~kJSCallerSaved) == 0); 121 ASSERT((object_regs & non_object_regs) == 0); 122 for (int i = 0; i < kNumJSCallerSaved; i++) { 123 int r = JSCallerSavedCode(i); 124 Register reg = { r }; 125 if ((object_regs & (1 << r)) != 0) { 126 __ push(reg); 127 } 128 if ((non_object_regs & (1 << r)) != 0) { 129 if (FLAG_debug_code) { 130 __ test(reg, Immediate(0xc0000000)); 131 __ Assert(zero, kUnableToEncodeValueAsSmi); 132 } 133 __ SmiTag(reg); 134 __ push(reg); 135 } 136 } 137 138 #ifdef DEBUG 139 __ RecordComment("// Calling from debug break to runtime - come in - over"); 140 #endif 141 __ Set(eax, Immediate(0)); // No arguments. 142 __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate()))); 143 144 CEntryStub ceb(1); 145 __ CallStub(&ceb); 146 147 // Automatically find register that could be used after register restore. 148 // We need one register for padding skip instructions. 149 Register unused_reg = { -1 }; 150 151 // Restore the register values containing object pointers from the 152 // expression stack. 153 for (int i = kNumJSCallerSaved; --i >= 0;) { 154 int r = JSCallerSavedCode(i); 155 Register reg = { r }; 156 if (FLAG_debug_code) { 157 __ Set(reg, Immediate(kDebugZapValue)); 158 } 159 bool taken = reg.code() == esi.code(); 160 if ((object_regs & (1 << r)) != 0) { 161 __ pop(reg); 162 taken = true; 163 } 164 if ((non_object_regs & (1 << r)) != 0) { 165 __ pop(reg); 166 __ SmiUntag(reg); 167 taken = true; 168 } 169 if (!taken) { 170 unused_reg = reg; 171 } 172 } 173 174 ASSERT(unused_reg.code() != -1); 175 176 // Read current padding counter and skip corresponding number of words. 177 __ pop(unused_reg); 178 // We divide stored value by 2 (untagging) and multiply it by word's size. 179 STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0); 180 __ lea(esp, Operand(esp, unused_reg, times_half_pointer_size, 0)); 181 182 // Get rid of the internal frame. 183 } 184 185 // If this call did not replace a call but patched other code then there will 186 // be an unwanted return address left on the stack. Here we get rid of that. 187 if (convert_call_to_jmp) { 188 __ add(esp, Immediate(kPointerSize)); 189 } 190 191 // Now that the break point has been handled, resume normal execution by 192 // jumping to the target address intended by the caller and that was 193 // overwritten by the address of DebugBreakXXX. 194 ExternalReference after_break_target = 195 ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate()); 196 __ jmp(Operand::StaticVariable(after_break_target)); 197 } 198 199 200 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { 201 // Register state for IC load call (from ic-ia32.cc). 202 // ----------- S t a t e ------------- 203 // -- ecx : name 204 // -- edx : receiver 205 // ----------------------------------- 206 Generate_DebugBreakCallHelper(masm, ecx.bit() | edx.bit(), 0, false); 207 } 208 209 210 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { 211 // Register state for IC store call (from ic-ia32.cc). 212 // ----------- S t a t e ------------- 213 // -- eax : value 214 // -- ecx : name 215 // -- edx : receiver 216 // ----------------------------------- 217 Generate_DebugBreakCallHelper( 218 masm, eax.bit() | ecx.bit() | edx.bit(), 0, false); 219 } 220 221 222 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { 223 // Register state for keyed IC load call (from ic-ia32.cc). 224 // ----------- S t a t e ------------- 225 // -- ecx : key 226 // -- edx : receiver 227 // ----------------------------------- 228 Generate_DebugBreakCallHelper(masm, ecx.bit() | edx.bit(), 0, false); 229 } 230 231 232 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { 233 // Register state for keyed IC load call (from ic-ia32.cc). 234 // ----------- S t a t e ------------- 235 // -- eax : value 236 // -- ecx : key 237 // -- edx : receiver 238 // ----------------------------------- 239 Generate_DebugBreakCallHelper( 240 masm, eax.bit() | ecx.bit() | edx.bit(), 0, false); 241 } 242 243 244 void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { 245 // Register state for CompareNil IC 246 // ----------- S t a t e ------------- 247 // -- eax : value 248 // ----------------------------------- 249 Generate_DebugBreakCallHelper(masm, eax.bit(), 0, false); 250 } 251 252 253 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { 254 // Register state for keyed IC call call (from ic-ia32.cc) 255 // ----------- S t a t e ------------- 256 // -- ecx: name 257 // ----------------------------------- 258 Generate_DebugBreakCallHelper(masm, ecx.bit(), 0, false); 259 } 260 261 262 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { 263 // Register state just before return from JS function (from codegen-ia32.cc). 264 // ----------- S t a t e ------------- 265 // -- eax: return value 266 // ----------------------------------- 267 Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true); 268 } 269 270 271 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { 272 // Register state for CallFunctionStub (from code-stubs-ia32.cc). 273 // ----------- S t a t e ------------- 274 // -- edi: function 275 // ----------------------------------- 276 Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false); 277 } 278 279 280 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { 281 // Register state for CallFunctionStub (from code-stubs-ia32.cc). 282 // ----------- S t a t e ------------- 283 // -- ebx: cache cell for call target 284 // -- edi: function 285 // ----------------------------------- 286 Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), 0, false); 287 } 288 289 290 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { 291 // Register state for CallConstructStub (from code-stubs-ia32.cc). 292 // eax is the actual number of arguments not encoded as a smi see comment 293 // above IC call. 294 // ----------- S t a t e ------------- 295 // -- eax: number of arguments (not smi) 296 // -- edi: constructor function 297 // ----------------------------------- 298 // The number of arguments in eax is not smi encoded. 299 Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false); 300 } 301 302 303 void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { 304 // Register state for CallConstructStub (from code-stubs-ia32.cc). 305 // eax is the actual number of arguments not encoded as a smi see comment 306 // above IC call. 307 // ----------- S t a t e ------------- 308 // -- eax: number of arguments (not smi) 309 // -- ebx: cache cell for call target 310 // -- edi: constructor function 311 // ----------------------------------- 312 // The number of arguments in eax is not smi encoded. 313 Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), eax.bit(), false); 314 } 315 316 317 void Debug::GenerateSlot(MacroAssembler* masm) { 318 // Generate enough nop's to make space for a call instruction. 319 Label check_codesize; 320 __ bind(&check_codesize); 321 __ RecordDebugBreakSlot(); 322 __ Nop(Assembler::kDebugBreakSlotLength); 323 ASSERT_EQ(Assembler::kDebugBreakSlotLength, 324 masm->SizeOfCodeGeneratedSince(&check_codesize)); 325 } 326 327 328 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { 329 // In the places where a debug break slot is inserted no registers can contain 330 // object pointers. 331 Generate_DebugBreakCallHelper(masm, 0, 0, true); 332 } 333 334 335 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 336 masm->ret(0); 337 } 338 339 340 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 341 ExternalReference restarter_frame_function_slot = 342 ExternalReference(Debug_Address::RestarterFrameFunctionPointer(), 343 masm->isolate()); 344 __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0)); 345 346 // We do not know our frame height, but set esp based on ebp. 347 __ lea(esp, Operand(ebp, -1 * kPointerSize)); 348 349 __ pop(edi); // Function. 350 __ pop(ebp); 351 352 // Load context from the function. 353 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 354 355 // Get function code. 356 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); 357 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); 358 __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); 359 360 // Re-run JSFunction, edi is function, esi is context. 361 __ jmp(edx); 362 } 363 364 const bool Debug::kFrameDropperSupported = true; 365 366 #undef __ 367 368 #endif // ENABLE_DEBUGGER_SUPPORT 369 370 } } // namespace v8::internal 371 372 #endif // V8_TARGET_ARCH_IA32 373