1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5 6 7#include "src/v8.h" 8 9#if V8_TARGET_ARCH_MIPS 10 11#include "src/codegen.h" 12#include "src/debug.h" 13 14namespace v8 { 15namespace internal { 16 17bool BreakLocationIterator::IsDebugBreakAtReturn() { 18 return Debug::IsDebugBreakAtReturn(rinfo()); 19} 20 21 22void BreakLocationIterator::SetDebugBreakAtReturn() { 23 // Mips return sequence: 24 // mov sp, fp 25 // lw fp, sp(0) 26 // lw ra, sp(4) 27 // addiu sp, sp, 8 28 // addiu sp, sp, N 29 // jr ra 30 // nop (in branch delay slot) 31 32 // Make sure this constant matches the number if instrucntions we emit. 33 DCHECK(Assembler::kJSReturnSequenceInstructions == 7); 34 CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions); 35 // li and Call pseudo-instructions emit two instructions each. 36 patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>( 37 debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry()))); 38 patcher.masm()->Call(v8::internal::t9); 39 patcher.masm()->nop(); 40 patcher.masm()->nop(); 41 patcher.masm()->nop(); 42 43 // TODO(mips): Open issue about using breakpoint instruction instead of nops. 44 // patcher.masm()->bkpt(0); 45} 46 47 48// Restore the JS frame exit code. 49void BreakLocationIterator::ClearDebugBreakAtReturn() { 50 rinfo()->PatchCode(original_rinfo()->pc(), 51 Assembler::kJSReturnSequenceInstructions); 52} 53 54 55// A debug break in the exit code is identified by the JS frame exit code 56// having been patched with li/call psuedo-instrunction (liu/ori/jalr). 57bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { 58 DCHECK(RelocInfo::IsJSReturn(rinfo->rmode())); 59 return rinfo->IsPatchedReturnSequence(); 60} 61 62 63bool BreakLocationIterator::IsDebugBreakAtSlot() { 64 DCHECK(IsDebugBreakSlot()); 65 // Check whether the debug break slot instructions have been patched. 66 return rinfo()->IsPatchedDebugBreakSlotSequence(); 67} 68 69 70void BreakLocationIterator::SetDebugBreakAtSlot() { 71 DCHECK(IsDebugBreakSlot()); 72 // Patch the code changing the debug break slot code from: 73 // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1) 74 // nop(DEBUG_BREAK_NOP) 75 // nop(DEBUG_BREAK_NOP) 76 // nop(DEBUG_BREAK_NOP) 77 // to a call to the debug break slot code. 78 // li t9, address (lui t9 / ori t9 instruction pair) 79 // call t9 (jalr t9 / nop instruction pair) 80 CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions); 81 patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>( 82 debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry()))); 83 patcher.masm()->Call(v8::internal::t9); 84} 85 86 87void BreakLocationIterator::ClearDebugBreakAtSlot() { 88 DCHECK(IsDebugBreakSlot()); 89 rinfo()->PatchCode(original_rinfo()->pc(), 90 Assembler::kDebugBreakSlotInstructions); 91} 92 93 94#define __ ACCESS_MASM(masm) 95 96 97 98static void Generate_DebugBreakCallHelper(MacroAssembler* masm, 99 RegList object_regs, 100 RegList non_object_regs) { 101 { 102 FrameScope scope(masm, StackFrame::INTERNAL); 103 104 // Load padding words on stack. 105 __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue))); 106 __ Subu(sp, sp, 107 Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize)); 108 for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) { 109 __ sw(at, MemOperand(sp, kPointerSize * i)); 110 } 111 __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize))); 112 __ push(at); 113 114 // Store the registers containing live values on the expression stack to 115 // make sure that these are correctly updated during GC. Non object values 116 // are stored as a smi causing it to be untouched by GC. 117 DCHECK((object_regs & ~kJSCallerSaved) == 0); 118 DCHECK((non_object_regs & ~kJSCallerSaved) == 0); 119 DCHECK((object_regs & non_object_regs) == 0); 120 if ((object_regs | non_object_regs) != 0) { 121 for (int i = 0; i < kNumJSCallerSaved; i++) { 122 int r = JSCallerSavedCode(i); 123 Register reg = { r }; 124 if ((non_object_regs & (1 << r)) != 0) { 125 if (FLAG_debug_code) { 126 __ And(at, reg, 0xc0000000); 127 __ Assert(eq, kUnableToEncodeValueAsSmi, at, Operand(zero_reg)); 128 } 129 __ sll(reg, reg, kSmiTagSize); 130 } 131 } 132 __ MultiPush(object_regs | non_object_regs); 133 } 134 135#ifdef DEBUG 136 __ RecordComment("// Calling from debug break to runtime - come in - over"); 137#endif 138 __ PrepareCEntryArgs(0); // No arguments. 139 __ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate())); 140 141 CEntryStub ceb(masm->isolate(), 1); 142 __ CallStub(&ceb); 143 144 // Restore the register values from the expression stack. 145 if ((object_regs | non_object_regs) != 0) { 146 __ MultiPop(object_regs | non_object_regs); 147 for (int i = 0; i < kNumJSCallerSaved; i++) { 148 int r = JSCallerSavedCode(i); 149 Register reg = { r }; 150 if ((non_object_regs & (1 << r)) != 0) { 151 __ srl(reg, reg, kSmiTagSize); 152 } 153 if (FLAG_debug_code && 154 (((object_regs |non_object_regs) & (1 << r)) == 0)) { 155 __ li(reg, kDebugZapValue); 156 } 157 } 158 } 159 160 // Don't bother removing padding bytes pushed on the stack 161 // as the frame is going to be restored right away. 162 163 // Leave the internal frame. 164 } 165 166 // Now that the break point has been handled, resume normal execution by 167 // jumping to the target address intended by the caller and that was 168 // overwritten by the address of DebugBreakXXX. 169 ExternalReference after_break_target = 170 ExternalReference::debug_after_break_target_address(masm->isolate()); 171 __ li(t9, Operand(after_break_target)); 172 __ lw(t9, MemOperand(t9)); 173 __ Jump(t9); 174} 175 176 177void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) { 178 // Register state for CallICStub 179 // ----------- S t a t e ------------- 180 // -- a1 : function 181 // -- a3 : slot in feedback array (smi) 182 // ----------------------------------- 183 Generate_DebugBreakCallHelper(masm, a1.bit() | a3.bit(), 0); 184} 185 186 187void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) { 188 Register receiver = LoadDescriptor::ReceiverRegister(); 189 Register name = LoadDescriptor::NameRegister(); 190 Generate_DebugBreakCallHelper(masm, receiver.bit() | name.bit(), 0); 191} 192 193 194void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) { 195 // Calling convention for IC store (from ic-mips.cc). 196 Register receiver = StoreDescriptor::ReceiverRegister(); 197 Register name = StoreDescriptor::NameRegister(); 198 Register value = StoreDescriptor::ValueRegister(); 199 Generate_DebugBreakCallHelper( 200 masm, receiver.bit() | name.bit() | value.bit(), 0); 201} 202 203 204void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { 205 // Calling convention for keyed IC load (from ic-mips.cc). 206 GenerateLoadICDebugBreak(masm); 207} 208 209 210void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { 211 // Calling convention for IC keyed store call (from ic-mips.cc). 212 Register receiver = StoreDescriptor::ReceiverRegister(); 213 Register name = StoreDescriptor::NameRegister(); 214 Register value = StoreDescriptor::ValueRegister(); 215 Generate_DebugBreakCallHelper( 216 masm, receiver.bit() | name.bit() | value.bit(), 0); 217} 218 219 220void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { 221 // Register state for CompareNil IC 222 // ----------- S t a t e ------------- 223 // -- a0 : value 224 // ----------------------------------- 225 Generate_DebugBreakCallHelper(masm, a0.bit(), 0); 226} 227 228 229void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) { 230 // In places other than IC call sites it is expected that v0 is TOS which 231 // is an object - this is not generally the case so this should be used with 232 // care. 233 Generate_DebugBreakCallHelper(masm, v0.bit(), 0); 234} 235 236 237void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { 238 // Register state for CallFunctionStub (from code-stubs-mips.cc). 239 // ----------- S t a t e ------------- 240 // -- a1 : function 241 // ----------------------------------- 242 Generate_DebugBreakCallHelper(masm, a1.bit(), 0); 243} 244 245 246void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { 247 // Calling convention for CallConstructStub (from code-stubs-mips.cc). 248 // ----------- S t a t e ------------- 249 // -- a0 : number of arguments (not smi) 250 // -- a1 : constructor function 251 // ----------------------------------- 252 Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit()); 253} 254 255 256void DebugCodegen::GenerateCallConstructStubRecordDebugBreak( 257 MacroAssembler* masm) { 258 // Calling convention for CallConstructStub (from code-stubs-mips.cc). 259 // ----------- S t a t e ------------- 260 // -- a0 : number of arguments (not smi) 261 // -- a1 : constructor function 262 // -- a2 : feedback array 263 // -- a3 : feedback slot (smi) 264 // ----------------------------------- 265 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit() | a3.bit(), a0.bit()); 266} 267 268 269void DebugCodegen::GenerateSlot(MacroAssembler* masm) { 270 // Generate enough nop's to make space for a call instruction. Avoid emitting 271 // the trampoline pool in the debug break slot code. 272 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm); 273 Label check_codesize; 274 __ bind(&check_codesize); 275 __ RecordDebugBreakSlot(); 276 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { 277 __ nop(MacroAssembler::DEBUG_BREAK_NOP); 278 } 279 DCHECK_EQ(Assembler::kDebugBreakSlotInstructions, 280 masm->InstructionsGeneratedSince(&check_codesize)); 281} 282 283 284void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) { 285 // In the places where a debug break slot is inserted no registers can contain 286 // object pointers. 287 Generate_DebugBreakCallHelper(masm, 0, 0); 288} 289 290 291void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 292 __ Ret(); 293} 294 295 296void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 297 ExternalReference restarter_frame_function_slot = 298 ExternalReference::debug_restarter_frame_function_pointer_address( 299 masm->isolate()); 300 __ li(at, Operand(restarter_frame_function_slot)); 301 __ sw(zero_reg, MemOperand(at, 0)); 302 303 // We do not know our frame height, but set sp based on fp. 304 __ Subu(sp, fp, Operand(kPointerSize)); 305 306 __ Pop(ra, fp, a1); // Return address, Frame, Function. 307 308 // Load context from the function. 309 __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); 310 311 // Get function code. 312 __ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); 313 __ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset)); 314 __ Addu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag)); 315 316 // Re-run JSFunction, a1 is function, cp is context. 317 __ Jump(t9); 318} 319 320 321const bool LiveEdit::kFrameDropperSupported = true; 322 323#undef __ 324 325} } // namespace v8::internal 326 327#endif // V8_TARGET_ARCH_MIPS 328