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 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 119const bool Debug::FramePaddingLayout::kIsSupported = false; 120 121 122#define __ ACCESS_MASM(masm) 123 124 125 126static 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 191void 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 205void 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 219void 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 228void 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 238void 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 247void 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 256void 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 264void 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 273void 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 283void 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 293void 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 304void 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 319void 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 326void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 327 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips); 328} 329 330 331void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 332 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips); 333} 334 335 336const 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