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 37namespace v8 { 38namespace internal { 39 40#ifdef ENABLE_DEBUGGER_SUPPORT 41 42bool BreakLocationIterator::IsDebugBreakAtReturn() { 43 return Debug::IsDebugBreakAtReturn(rinfo()); 44} 45 46 47void 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. 75void 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). 83bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { 84 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); 85 return rinfo->IsPatchedReturnSequence(); 86} 87 88 89bool BreakLocationIterator::IsDebugBreakAtSlot() { 90 ASSERT(IsDebugBreakSlot()); 91 // Check whether the debug break slot instructions have been patched. 92 return rinfo()->IsPatchedDebugBreakSlotSequence(); 93} 94 95 96void 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 113void 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 124static 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 190void 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 204void 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 218void 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 227void 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 237void 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 246void 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 254void 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 263void 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 273void 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 283void 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 294void 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 309void 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 316void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 317 masm->Abort("LiveEdit frame dropping is not supported on mips"); 318} 319 320 321void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 322 masm->Abort("LiveEdit frame dropping is not supported on mips"); 323} 324 325 326const 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