1 // Copyright 2011 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 defined(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 120 #define __ ACCESS_MASM(masm) 121 122 123 124 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, 125 RegList object_regs, 126 RegList non_object_regs) { 127 { 128 FrameScope scope(masm, StackFrame::INTERNAL); 129 130 // Store the registers containing live values on the expression stack to 131 // make sure that these are correctly updated during GC. Non object values 132 // are stored as a smi causing it to be untouched by GC. 133 ASSERT((object_regs & ~kJSCallerSaved) == 0); 134 ASSERT((non_object_regs & ~kJSCallerSaved) == 0); 135 ASSERT((object_regs & non_object_regs) == 0); 136 if ((object_regs | non_object_regs) != 0) { 137 for (int i = 0; i < kNumJSCallerSaved; i++) { 138 int r = JSCallerSavedCode(i); 139 Register reg = { r }; 140 if ((non_object_regs & (1 << r)) != 0) { 141 if (FLAG_debug_code) { 142 __ And(at, reg, 0xc0000000); 143 __ Assert( 144 eq, "Unable to encode value as smi", at, Operand(zero_reg)); 145 } 146 __ sll(reg, reg, kSmiTagSize); 147 } 148 } 149 __ MultiPush(object_regs | non_object_regs); 150 } 151 152 #ifdef DEBUG 153 __ RecordComment("// Calling from debug break to runtime - come in - over"); 154 #endif 155 __ PrepareCEntryArgs(0); // No arguments. 156 __ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate())); 157 158 CEntryStub ceb(1); 159 __ CallStub(&ceb); 160 161 // Restore the register values from the expression stack. 162 if ((object_regs | non_object_regs) != 0) { 163 __ MultiPop(object_regs | non_object_regs); 164 for (int i = 0; i < kNumJSCallerSaved; i++) { 165 int r = JSCallerSavedCode(i); 166 Register reg = { r }; 167 if ((non_object_regs & (1 << r)) != 0) { 168 __ srl(reg, reg, kSmiTagSize); 169 } 170 if (FLAG_debug_code && 171 (((object_regs |non_object_regs) & (1 << r)) == 0)) { 172 __ li(reg, kDebugZapValue); 173 } 174 } 175 } 176 177 // Leave the internal frame. 178 } 179 180 // Now that the break point has been handled, resume normal execution by 181 // jumping to the target address intended by the caller and that was 182 // overwritten by the address of DebugBreakXXX. 183 __ li(t9, Operand( 184 ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate()))); 185 __ lw(t9, MemOperand(t9)); 186 __ Jump(t9); 187 } 188 189 190 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { 191 // Calling convention for IC load (from ic-mips.cc). 192 // ----------- S t a t e ------------- 193 // -- a2 : name 194 // -- ra : return address 195 // -- a0 : receiver 196 // -- [sp] : receiver 197 // ----------------------------------- 198 // Registers a0 and a2 contain objects that need to be pushed on the 199 // expression stack of the fake JS frame. 200 Generate_DebugBreakCallHelper(masm, a0.bit() | a2.bit(), 0); 201 } 202 203 204 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { 205 // Calling convention for IC store (from ic-mips.cc). 206 // ----------- S t a t e ------------- 207 // -- a0 : value 208 // -- a1 : receiver 209 // -- a2 : name 210 // -- ra : return address 211 // ----------------------------------- 212 // Registers a0, a1, and a2 contain objects that need to be pushed on the 213 // expression stack of the fake JS frame. 214 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0); 215 } 216 217 218 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { 219 // ---------- S t a t e -------------- 220 // -- ra : return address 221 // -- a0 : key 222 // -- a1 : receiver 223 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit(), 0); 224 } 225 226 227 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { 228 // ---------- S t a t e -------------- 229 // -- a0 : value 230 // -- a1 : key 231 // -- a2 : receiver 232 // -- ra : return address 233 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0); 234 } 235 236 237 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { 238 // Calling convention for IC call (from ic-mips.cc). 239 // ----------- S t a t e ------------- 240 // -- a2: name 241 // ----------------------------------- 242 Generate_DebugBreakCallHelper(masm, a2.bit(), 0); 243 } 244 245 246 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { 247 // In places other than IC call sites it is expected that v0 is TOS which 248 // is an object - this is not generally the case so this should be used with 249 // care. 250 Generate_DebugBreakCallHelper(masm, v0.bit(), 0); 251 } 252 253 254 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { 255 // Register state for CallFunctionStub (from code-stubs-mips.cc). 256 // ----------- S t a t e ------------- 257 // -- a1 : function 258 // ----------------------------------- 259 Generate_DebugBreakCallHelper(masm, a1.bit(), 0); 260 } 261 262 263 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { 264 // Register state for CallFunctionStub (from code-stubs-mips.cc). 265 // ----------- S t a t e ------------- 266 // -- a1 : function 267 // -- a2 : cache cell for call target 268 // ----------------------------------- 269 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), 0); 270 } 271 272 273 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { 274 // Calling convention for CallConstructStub (from code-stubs-mips.cc). 275 // ----------- S t a t e ------------- 276 // -- a0 : number of arguments (not smi) 277 // -- a1 : constructor function 278 // ----------------------------------- 279 Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit()); 280 } 281 282 283 void Debug::GenerateCallConstructStubRecordDebugBreak(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 // -- a2 : cache cell for call target 289 // ----------------------------------- 290 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), a0.bit()); 291 } 292 293 294 void Debug::GenerateSlot(MacroAssembler* masm) { 295 // Generate enough nop's to make space for a call instruction. Avoid emitting 296 // the trampoline pool in the debug break slot code. 297 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm); 298 Label check_codesize; 299 __ bind(&check_codesize); 300 __ RecordDebugBreakSlot(); 301 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { 302 __ nop(MacroAssembler::DEBUG_BREAK_NOP); 303 } 304 ASSERT_EQ(Assembler::kDebugBreakSlotInstructions, 305 masm->InstructionsGeneratedSince(&check_codesize)); 306 } 307 308 309 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { 310 // In the places where a debug break slot is inserted no registers can contain 311 // object pointers. 312 Generate_DebugBreakCallHelper(masm, 0, 0); 313 } 314 315 316 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 317 masm->Abort("LiveEdit frame dropping is not supported on mips"); 318 } 319 320 321 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 322 masm->Abort("LiveEdit frame dropping is not supported on mips"); 323 } 324 325 326 const bool Debug::kFrameDropperSupported = false; 327 328 #undef __ 329 330 331 #endif // ENABLE_DEBUGGER_SUPPORT 332 333 } } // namespace v8::internal 334 335 #endif // V8_TARGET_ARCH_MIPS 336