Home | History | Annotate | Download | only in arm64
      1 // Copyright 2013 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 #include "src/v8.h"
      6 
      7 #if V8_TARGET_ARCH_ARM64
      8 
      9 #include "src/codegen.h"
     10 #include "src/debug.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 
     16 #define __ ACCESS_MASM(masm)
     17 
     18 bool BreakLocationIterator::IsDebugBreakAtReturn() {
     19   return Debug::IsDebugBreakAtReturn(rinfo());
     20 }
     21 
     22 
     23 void BreakLocationIterator::SetDebugBreakAtReturn() {
     24   // Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing
     25   // the return from JS function sequence from
     26   //   mov sp, fp
     27   //   ldp fp, lr, [sp] #16
     28   //   lrd ip0, [pc, #(3 * kInstructionSize)]
     29   //   add sp, sp, ip0
     30   //   ret
     31   //   <number of paramters ...
     32   //    ... plus one (64 bits)>
     33   // to a call to the debug break return code.
     34   //   ldr ip0, [pc, #(3 * kInstructionSize)]
     35   //   blr ip0
     36   //   hlt kHltBadCode    @ code should not return, catch if it does.
     37   //   <debug break return code ...
     38   //    ... entry point address (64 bits)>
     39 
     40   // The patching code must not overflow the space occupied by the return
     41   // sequence.
     42   STATIC_ASSERT(Assembler::kJSRetSequenceInstructions >= 5);
     43   PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 5);
     44   byte* entry =
     45       debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry();
     46 
     47   // The first instruction of a patched return sequence must be a load literal
     48   // loading the address of the debug break return code.
     49   patcher.ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
     50   // TODO(all): check the following is correct.
     51   // The debug break return code will push a frame and call statically compiled
     52   // code. By using blr, even though control will not return after the branch,
     53   // this call site will be registered in the frame (lr being saved as the pc
     54   // of the next instruction to execute for this frame). The debugger can now
     55   // iterate on the frames to find call to debug break return code.
     56   patcher.blr(ip0);
     57   patcher.hlt(kHltBadCode);
     58   patcher.dc64(reinterpret_cast<int64_t>(entry));
     59 }
     60 
     61 
     62 void BreakLocationIterator::ClearDebugBreakAtReturn() {
     63   // Reset the code emitted by EmitReturnSequence to its original state.
     64   rinfo()->PatchCode(original_rinfo()->pc(),
     65                      Assembler::kJSRetSequenceInstructions);
     66 }
     67 
     68 
     69 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
     70   ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
     71   return rinfo->IsPatchedReturnSequence();
     72 }
     73 
     74 
     75 bool BreakLocationIterator::IsDebugBreakAtSlot() {
     76   ASSERT(IsDebugBreakSlot());
     77   // Check whether the debug break slot instructions have been patched.
     78   return rinfo()->IsPatchedDebugBreakSlotSequence();
     79 }
     80 
     81 
     82 void BreakLocationIterator::SetDebugBreakAtSlot() {
     83   // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
     84   // break slot code from
     85   //   mov x0, x0    @ nop DEBUG_BREAK_NOP
     86   //   mov x0, x0    @ nop DEBUG_BREAK_NOP
     87   //   mov x0, x0    @ nop DEBUG_BREAK_NOP
     88   //   mov x0, x0    @ nop DEBUG_BREAK_NOP
     89   // to a call to the debug slot code.
     90   //   ldr ip0, [pc, #(2 * kInstructionSize)]
     91   //   blr ip0
     92   //   <debug break slot code ...
     93   //    ... entry point address (64 bits)>
     94 
     95   // TODO(all): consider adding a hlt instruction after the blr as we don't
     96   // expect control to return here. This implies increasing
     97   // kDebugBreakSlotInstructions to 5 instructions.
     98 
     99   // The patching code must not overflow the space occupied by the return
    100   // sequence.
    101   STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4);
    102   PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 4);
    103   byte* entry =
    104       debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry();
    105 
    106   // The first instruction of a patched debug break slot must be a load literal
    107   // loading the address of the debug break slot code.
    108   patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
    109   // TODO(all): check the following is correct.
    110   // The debug break slot code will push a frame and call statically compiled
    111   // code. By using blr, event hough control will not return after the branch,
    112   // this call site will be registered in the frame (lr being saved as the pc
    113   // of the next instruction to execute for this frame). The debugger can now
    114   // iterate on the frames to find call to debug break slot code.
    115   patcher.blr(ip0);
    116   patcher.dc64(reinterpret_cast<int64_t>(entry));
    117 }
    118 
    119 
    120 void BreakLocationIterator::ClearDebugBreakAtSlot() {
    121   ASSERT(IsDebugBreakSlot());
    122   rinfo()->PatchCode(original_rinfo()->pc(),
    123                      Assembler::kDebugBreakSlotInstructions);
    124 }
    125 
    126 
    127 static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
    128                                           RegList object_regs,
    129                                           RegList non_object_regs,
    130                                           Register scratch) {
    131   {
    132     FrameScope scope(masm, StackFrame::INTERNAL);
    133 
    134     // Any live values (object_regs and non_object_regs) in caller-saved
    135     // registers (or lr) need to be stored on the stack so that their values are
    136     // safely preserved for a call into C code.
    137     //
    138     // Also:
    139     //  * object_regs may be modified during the C code by the garbage
    140     //    collector. Every object register must be a valid tagged pointer or
    141     //    SMI.
    142     //
    143     //  * non_object_regs will be converted to SMIs so that the garbage
    144     //    collector doesn't try to interpret them as pointers.
    145     //
    146     // TODO(jbramley): Why can't this handle callee-saved registers?
    147     ASSERT((~kCallerSaved.list() & object_regs) == 0);
    148     ASSERT((~kCallerSaved.list() & non_object_regs) == 0);
    149     ASSERT((object_regs & non_object_regs) == 0);
    150     ASSERT((scratch.Bit() & object_regs) == 0);
    151     ASSERT((scratch.Bit() & non_object_regs) == 0);
    152     ASSERT((masm->TmpList()->list() & (object_regs | non_object_regs)) == 0);
    153     STATIC_ASSERT(kSmiValueSize == 32);
    154 
    155     CPURegList non_object_list =
    156         CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs);
    157     while (!non_object_list.IsEmpty()) {
    158       // Store each non-object register as two SMIs.
    159       Register reg = Register(non_object_list.PopLowestIndex());
    160       __ Lsr(scratch, reg, 32);
    161       __ SmiTagAndPush(scratch, reg);
    162 
    163       // Stack:
    164       //  jssp[12]: reg[63:32]
    165       //  jssp[8]: 0x00000000 (SMI tag & padding)
    166       //  jssp[4]: reg[31:0]
    167       //  jssp[0]: 0x00000000 (SMI tag & padding)
    168       STATIC_ASSERT((kSmiTag == 0) && (kSmiShift == 32));
    169     }
    170 
    171     if (object_regs != 0) {
    172       __ PushXRegList(object_regs);
    173     }
    174 
    175 #ifdef DEBUG
    176     __ RecordComment("// Calling from debug break to runtime - come in - over");
    177 #endif
    178     __ Mov(x0, 0);  // No arguments.
    179     __ Mov(x1, ExternalReference::debug_break(masm->isolate()));
    180 
    181     CEntryStub stub(masm->isolate(), 1);
    182     __ CallStub(&stub);
    183 
    184     // Restore the register values from the expression stack.
    185     if (object_regs != 0) {
    186       __ PopXRegList(object_regs);
    187     }
    188 
    189     non_object_list =
    190         CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs);
    191     while (!non_object_list.IsEmpty()) {
    192       // Load each non-object register from two SMIs.
    193       // Stack:
    194       //  jssp[12]: reg[63:32]
    195       //  jssp[8]: 0x00000000 (SMI tag & padding)
    196       //  jssp[4]: reg[31:0]
    197       //  jssp[0]: 0x00000000 (SMI tag & padding)
    198       Register reg = Register(non_object_list.PopHighestIndex());
    199       __ Pop(scratch, reg);
    200       __ Bfxil(reg, scratch, 32, 32);
    201     }
    202 
    203     // Leave the internal frame.
    204   }
    205 
    206   // Now that the break point has been handled, resume normal execution by
    207   // jumping to the target address intended by the caller and that was
    208   // overwritten by the address of DebugBreakXXX.
    209   ExternalReference after_break_target =
    210       ExternalReference::debug_after_break_target_address(masm->isolate());
    211   __ Mov(scratch, after_break_target);
    212   __ Ldr(scratch, MemOperand(scratch));
    213   __ Br(scratch);
    214 }
    215 
    216 
    217 void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
    218   // Register state for CallICStub
    219   // ----------- S t a t e -------------
    220   //  -- x1 : function
    221   //  -- x3 : slot in feedback array
    222   // -----------------------------------
    223   Generate_DebugBreakCallHelper(masm, x1.Bit() | x3.Bit(), 0, x10);
    224 }
    225 
    226 
    227 void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) {
    228   // Calling convention for IC load (from ic-arm.cc).
    229   // ----------- S t a t e -------------
    230   //  -- x2    : name
    231   //  -- lr    : return address
    232   //  -- x0    : receiver
    233   //  -- [sp]  : receiver
    234   // -----------------------------------
    235   // Registers x0 and x2 contain objects that need to be pushed on the
    236   // expression stack of the fake JS frame.
    237   Generate_DebugBreakCallHelper(masm, x0.Bit() | x2.Bit(), 0, x10);
    238 }
    239 
    240 
    241 void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) {
    242   // Calling convention for IC store (from ic-arm.cc).
    243   // ----------- S t a t e -------------
    244   //  -- x0    : value
    245   //  -- x1    : receiver
    246   //  -- x2    : name
    247   //  -- lr    : return address
    248   // -----------------------------------
    249   // Registers x0, x1, and x2 contain objects that need to be pushed on the
    250   // expression stack of the fake JS frame.
    251   Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10);
    252 }
    253 
    254 
    255 void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
    256   // ---------- S t a t e --------------
    257   //  -- lr     : return address
    258   //  -- x0     : key
    259   //  -- x1     : receiver
    260   Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit(), 0, x10);
    261 }
    262 
    263 
    264 void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
    265   // ---------- S t a t e --------------
    266   //  -- x0     : value
    267   //  -- x1     : key
    268   //  -- x2     : receiver
    269   //  -- lr     : return address
    270   Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10);
    271 }
    272 
    273 
    274 void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
    275   // Register state for CompareNil IC
    276   // ----------- S t a t e -------------
    277   //  -- r0    : value
    278   // -----------------------------------
    279   Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10);
    280 }
    281 
    282 
    283 void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
    284   // In places other than IC call sites it is expected that r0 is TOS which
    285   // is an object - this is not generally the case so this should be used with
    286   // care.
    287   Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10);
    288 }
    289 
    290 
    291 void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
    292   // Register state for CallFunctionStub (from code-stubs-arm64.cc).
    293   // ----------- S t a t e -------------
    294   //  -- x1 : function
    295   // -----------------------------------
    296   Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10);
    297 }
    298 
    299 
    300 void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
    301   // Calling convention for CallConstructStub (from code-stubs-arm64.cc).
    302   // ----------- S t a t e -------------
    303   //  -- x0 : number of arguments (not smi)
    304   //  -- x1 : constructor function
    305   // -----------------------------------
    306   Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10);
    307 }
    308 
    309 
    310 void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
    311     MacroAssembler* masm) {
    312   // Calling convention for CallConstructStub (from code-stubs-arm64.cc).
    313   // ----------- S t a t e -------------
    314   //  -- x0 : number of arguments (not smi)
    315   //  -- x1 : constructor function
    316   //  -- x2     : feedback array
    317   //  -- x3     : feedback slot (smi)
    318   // -----------------------------------
    319   Generate_DebugBreakCallHelper(
    320       masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10);
    321 }
    322 
    323 
    324 void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
    325   // Generate enough nop's to make space for a call instruction. Avoid emitting
    326   // the constant pool in the debug break slot code.
    327   InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
    328 
    329   __ RecordDebugBreakSlot();
    330   for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
    331     __ nop(Assembler::DEBUG_BREAK_NOP);
    332   }
    333 }
    334 
    335 
    336 void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
    337   // In the places where a debug break slot is inserted no registers can contain
    338   // object pointers.
    339   Generate_DebugBreakCallHelper(masm, 0, 0, x10);
    340 }
    341 
    342 
    343 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
    344   masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnARM64);
    345 }
    346 
    347 
    348 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
    349   masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnARM64);
    350 }
    351 
    352 
    353 const bool LiveEdit::kFrameDropperSupported = false;
    354 
    355 } }  // namespace v8::internal
    356 
    357 #endif  // V8_TARGET_ARCH_ARM64
    358