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 29 30 #include "v8.h" 31 32 #if V8_TARGET_ARCH_MIPS 33 34 #include "codegen.h" 35 #include "debug.h" 36 37 namespace v8 { 38 namespace internal { 39 40 #ifdef ENABLE_DEBUGGER_SUPPORT 41 42 bool BreakLocationIterator::IsDebugBreakAtReturn() { 43 return Debug::IsDebugBreakAtReturn(rinfo()); 44 } 45 46 47 void BreakLocationIterator::SetDebugBreakAtReturn() { 48 // Mips return sequence: 49 // mov sp, fp 50 // lw fp, sp(0) 51 // lw ra, sp(4) 52 // addiu sp, sp, 8 53 // addiu sp, sp, N 54 // jr ra 55 // nop (in branch delay slot) 56 57 // Make sure this constant matches the number if instrucntions we emit. 58 ASSERT(Assembler::kJSReturnSequenceInstructions == 7); 59 CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions); 60 // li and Call pseudo-instructions emit two instructions each. 61 patcher.masm()->li(v8::internal::t9, 62 Operand(reinterpret_cast<int32_t>( 63 Isolate::Current()->debug()->debug_break_return()->entry()))); 64 patcher.masm()->Call(v8::internal::t9); 65 patcher.masm()->nop(); 66 patcher.masm()->nop(); 67 patcher.masm()->nop(); 68 69 // TODO(mips): Open issue about using breakpoint instruction instead of nops. 70 // patcher.masm()->bkpt(0); 71 } 72 73 74 // Restore the JS frame exit code. 75 void BreakLocationIterator::ClearDebugBreakAtReturn() { 76 rinfo()->PatchCode(original_rinfo()->pc(), 77 Assembler::kJSReturnSequenceInstructions); 78 } 79 80 81 // A debug break in the exit code is identified by the JS frame exit code 82 // having been patched with li/call psuedo-instrunction (liu/ori/jalr). 83 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { 84 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); 85 return rinfo->IsPatchedReturnSequence(); 86 } 87 88 89 bool BreakLocationIterator::IsDebugBreakAtSlot() { 90 ASSERT(IsDebugBreakSlot()); 91 // Check whether the debug break slot instructions have been patched. 92 return rinfo()->IsPatchedDebugBreakSlotSequence(); 93 } 94 95 96 void BreakLocationIterator::SetDebugBreakAtSlot() { 97 ASSERT(IsDebugBreakSlot()); 98 // Patch the code changing the debug break slot code from: 99 // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1) 100 // nop(DEBUG_BREAK_NOP) 101 // nop(DEBUG_BREAK_NOP) 102 // nop(DEBUG_BREAK_NOP) 103 // to a call to the debug break slot code. 104 // li t9, address (lui t9 / ori t9 instruction pair) 105 // call t9 (jalr t9 / nop instruction pair) 106 CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions); 107 patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>( 108 Isolate::Current()->debug()->debug_break_slot()->entry()))); 109 patcher.masm()->Call(v8::internal::t9); 110 } 111 112 113 void BreakLocationIterator::ClearDebugBreakAtSlot() { 114 ASSERT(IsDebugBreakSlot()); 115 rinfo()->PatchCode(original_rinfo()->pc(), 116 Assembler::kDebugBreakSlotInstructions); 117 } 118 119 const bool Debug::FramePaddingLayout::kIsSupported = false; 120 121 122 #define __ ACCESS_MASM(masm) 123 124 125 126 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, 127 RegList object_regs, 128 RegList non_object_regs) { 129 { 130 FrameScope scope(masm, StackFrame::INTERNAL); 131 132 // Store the registers containing live values on the expression stack to 133 // make sure that these are correctly updated during GC. Non object values 134 // are stored as a smi causing it to be untouched by GC. 135 ASSERT((object_regs & ~kJSCallerSaved) == 0); 136 ASSERT((non_object_regs & ~kJSCallerSaved) == 0); 137 ASSERT((object_regs & non_object_regs) == 0); 138 if ((object_regs | non_object_regs) != 0) { 139 for (int i = 0; i < kNumJSCallerSaved; i++) { 140 int r = JSCallerSavedCode(i); 141 Register reg = { r }; 142 if ((non_object_regs & (1 << r)) != 0) { 143 if (FLAG_debug_code) { 144 __ And(at, reg, 0xc0000000); 145 __ Assert(eq, kUnableToEncodeValueAsSmi, at, Operand(zero_reg)); 146 } 147 __ sll(reg, reg, kSmiTagSize); 148 } 149 } 150 __ MultiPush(object_regs | non_object_regs); 151 } 152 153 #ifdef DEBUG 154 __ RecordComment("// Calling from debug break to runtime - come in - over"); 155 #endif 156 __ PrepareCEntryArgs(0); // No arguments. 157 __ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate())); 158 159 CEntryStub ceb(1); 160 __ CallStub(&ceb); 161 162 // Restore the register values from the expression stack. 163 if ((object_regs | non_object_regs) != 0) { 164 __ MultiPop(object_regs | non_object_regs); 165 for (int i = 0; i < kNumJSCallerSaved; i++) { 166 int r = JSCallerSavedCode(i); 167 Register reg = { r }; 168 if ((non_object_regs & (1 << r)) != 0) { 169 __ srl(reg, reg, kSmiTagSize); 170 } 171 if (FLAG_debug_code && 172 (((object_regs |non_object_regs) & (1 << r)) == 0)) { 173 __ li(reg, kDebugZapValue); 174 } 175 } 176 } 177 178 // Leave the internal frame. 179 } 180 181 // Now that the break point has been handled, resume normal execution by 182 // jumping to the target address intended by the caller and that was 183 // overwritten by the address of DebugBreakXXX. 184 __ li(t9, Operand( 185 ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate()))); 186 __ lw(t9, MemOperand(t9)); 187 __ Jump(t9); 188 } 189 190 191 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { 192 // Calling convention for IC load (from ic-mips.cc). 193 // ----------- S t a t e ------------- 194 // -- a2 : name 195 // -- ra : return address 196 // -- a0 : receiver 197 // -- [sp] : receiver 198 // ----------------------------------- 199 // Registers a0 and a2 contain objects that need to be pushed on the 200 // expression stack of the fake JS frame. 201 Generate_DebugBreakCallHelper(masm, a0.bit() | a2.bit(), 0); 202 } 203 204 205 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { 206 // Calling convention for IC store (from ic-mips.cc). 207 // ----------- S t a t e ------------- 208 // -- a0 : value 209 // -- a1 : receiver 210 // -- a2 : name 211 // -- ra : return address 212 // ----------------------------------- 213 // Registers a0, a1, and a2 contain objects that need to be pushed on the 214 // expression stack of the fake JS frame. 215 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0); 216 } 217 218 219 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { 220 // ---------- S t a t e -------------- 221 // -- ra : return address 222 // -- a0 : key 223 // -- a1 : receiver 224 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit(), 0); 225 } 226 227 228 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { 229 // ---------- S t a t e -------------- 230 // -- a0 : value 231 // -- a1 : key 232 // -- a2 : receiver 233 // -- ra : return address 234 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0); 235 } 236 237 238 void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { 239 // Register state for CompareNil IC 240 // ----------- S t a t e ------------- 241 // -- a0 : value 242 // ----------------------------------- 243 Generate_DebugBreakCallHelper(masm, a0.bit(), 0); 244 } 245 246 247 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { 248 // Calling convention for IC call (from ic-mips.cc). 249 // ----------- S t a t e ------------- 250 // -- a2: name 251 // ----------------------------------- 252 Generate_DebugBreakCallHelper(masm, a2.bit(), 0); 253 } 254 255 256 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { 257 // In places other than IC call sites it is expected that v0 is TOS which 258 // is an object - this is not generally the case so this should be used with 259 // care. 260 Generate_DebugBreakCallHelper(masm, v0.bit(), 0); 261 } 262 263 264 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { 265 // Register state for CallFunctionStub (from code-stubs-mips.cc). 266 // ----------- S t a t e ------------- 267 // -- a1 : function 268 // ----------------------------------- 269 Generate_DebugBreakCallHelper(masm, a1.bit(), 0); 270 } 271 272 273 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { 274 // Register state for CallFunctionStub (from code-stubs-mips.cc). 275 // ----------- S t a t e ------------- 276 // -- a1 : function 277 // -- a2 : cache cell for call target 278 // ----------------------------------- 279 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), 0); 280 } 281 282 283 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { 284 // Calling convention for CallConstructStub (from code-stubs-mips.cc). 285 // ----------- S t a t e ------------- 286 // -- a0 : number of arguments (not smi) 287 // -- a1 : constructor function 288 // ----------------------------------- 289 Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit()); 290 } 291 292 293 void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { 294 // Calling convention for CallConstructStub (from code-stubs-mips.cc). 295 // ----------- S t a t e ------------- 296 // -- a0 : number of arguments (not smi) 297 // -- a1 : constructor function 298 // -- a2 : cache cell for call target 299 // ----------------------------------- 300 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), a0.bit()); 301 } 302 303 304 void Debug::GenerateSlot(MacroAssembler* masm) { 305 // Generate enough nop's to make space for a call instruction. Avoid emitting 306 // the trampoline pool in the debug break slot code. 307 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm); 308 Label check_codesize; 309 __ bind(&check_codesize); 310 __ RecordDebugBreakSlot(); 311 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { 312 __ nop(MacroAssembler::DEBUG_BREAK_NOP); 313 } 314 ASSERT_EQ(Assembler::kDebugBreakSlotInstructions, 315 masm->InstructionsGeneratedSince(&check_codesize)); 316 } 317 318 319 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { 320 // In the places where a debug break slot is inserted no registers can contain 321 // object pointers. 322 Generate_DebugBreakCallHelper(masm, 0, 0); 323 } 324 325 326 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 327 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips); 328 } 329 330 331 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 332 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips); 333 } 334 335 336 const bool Debug::kFrameDropperSupported = false; 337 338 #undef __ 339 340 341 #endif // ENABLE_DEBUGGER_SUPPORT 342 343 } } // namespace v8::internal 344 345 #endif // V8_TARGET_ARCH_MIPS 346