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