Home | History | Annotate | Download | only in mips64
      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 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 bool BreakLocationIterator::IsDebugBreakAtReturn() {
     18   return Debug::IsDebugBreakAtReturn(rinfo());
     19 }
     20 
     21 
     22 void 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.
     48 void 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).
     56 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
     57   DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
     58   return rinfo->IsPatchedReturnSequence();
     59 }
     60 
     61 
     62 bool BreakLocationIterator::IsDebugBreakAtSlot() {
     63   DCHECK(IsDebugBreakSlot());
     64   // Check whether the debug break slot instructions have been patched.
     65   return rinfo()->IsPatchedDebugBreakSlotSequence();
     66 }
     67 
     68 
     69 void 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 
     90 void 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 
    101 static 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 
    180 void 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 
    190 void 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 
    197 void 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 
    206 void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
    207   // Calling convention for keyed IC load (from ic-mips64.cc).
    208   GenerateLoadICDebugBreak(masm);
    209 }
    210 
    211 
    212 void 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 
    222 void 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 
    231 void 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 
    239 void 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 
    248 void 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 
    259 void 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 
    272 void 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 
    287 void 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 
    294 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
    295   __ Ret();
    296 }
    297 
    298 
    299 void 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 
    324 const bool LiveEdit::kFrameDropperSupported = true;
    325 
    326 #undef __
    327 
    328 } }  // namespace v8::internal
    329 
    330 #endif  // V8_TARGET_ARCH_MIPS64
    331