2007-06-09 Markus Deuling <deuling@de.ibm.com>
[external/binutils.git] / gdb / rs6000-tdep.c
index cc2f074..50557c0 100644 (file)
@@ -40,6 +40,7 @@
 #include "sim-regno.h"
 #include "gdb/sim-ppc.h"
 #include "reggroups.h"
+#include "dwarf2-frame.h"
 
 #include "libbfd.h"            /* for bfd_default_set_arch_mach */
 #include "coff/internal.h"     /* for libcoff.h */
@@ -288,7 +289,9 @@ rs6000_register_sim_regno (int reg)
   struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
   int sim_regno;
 
-  gdb_assert (0 <= reg && reg <= NUM_REGS + NUM_PSEUDO_REGS);
+  gdb_assert (0 <= reg 
+             && reg <= gdbarch_num_regs (current_gdbarch)
+                       + gdbarch_num_pseudo_regs (current_gdbarch));
   sim_regno = tdep->sim_regno[reg];
 
   if (sim_regno >= 0)
@@ -610,19 +613,6 @@ rs6000_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
   return 0;
 }
 
-
-/* Fill in fi->saved_regs */
-
-struct frame_extra_info
-{
-  /* Functions calling alloca() change the value of the stack
-     pointer. We need to use initial stack pointer (which is saved in
-     r31 by gcc) in such cases. If a compiler emits traceback table,
-     then we should use the alloca register specified in traceback
-     table. FIXME. */
-  CORE_ADDR initial_sp;                /* initial stack pointer. */
-};
-
 /* Get the ith function argument for the current function.  */
 static CORE_ADDR
 rs6000_fetch_pointer_argument (struct frame_info *frame, int argi, 
@@ -712,18 +702,112 @@ rs6000_breakpoint_from_pc (CORE_ADDR *bp_addr, int *bp_size)
   static unsigned char big_breakpoint[] = { 0x7d, 0x82, 0x10, 0x08 };
   static unsigned char little_breakpoint[] = { 0x08, 0x10, 0x82, 0x7d };
   *bp_size = 4;
-  if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+  if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
     return big_breakpoint;
   else
     return little_breakpoint;
 }
 
 
-/* AIX does not support PT_STEP. Simulate it. */
+/* Instruction masks used during single-stepping of atomic sequences.  */
+#define LWARX_MASK 0xfc0007fe
+#define LWARX_INSTRUCTION 0x7c000028
+#define LDARX_INSTRUCTION 0x7c0000A8
+#define STWCX_MASK 0xfc0007ff
+#define STWCX_INSTRUCTION 0x7c00012d
+#define STDCX_INSTRUCTION 0x7c0001ad
+#define BC_MASK 0xfc000000
+#define BC_INSTRUCTION 0x40000000
+
+/* Checks for an atomic sequence of instructions beginning with a LWARX/LDARX
+   instruction and ending with a STWCX/STDCX instruction.  If such a sequence
+   is found, attempt to step through it.  A breakpoint is placed at the end of 
+   the sequence.  */
+
+static int 
+deal_with_atomic_sequence (struct regcache *regcache)
+{
+  CORE_ADDR pc = read_pc ();
+  CORE_ADDR breaks[2] = {-1, -1};
+  CORE_ADDR loc = pc;
+  CORE_ADDR branch_bp; /* Breakpoint at branch instruction's destination.  */
+  CORE_ADDR closing_insn; /* Instruction that closes the atomic sequence.  */
+  int insn = read_memory_integer (loc, PPC_INSN_SIZE);
+  int insn_count;
+  int index;
+  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */  
+  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
+  int opcode; /* Branch instruction's OPcode.  */
+  int bc_insn_count = 0; /* Conditional branch instruction count.  */
+
+  /* Assume all atomic sequences start with a lwarx/ldarx instruction.  */
+  if ((insn & LWARX_MASK) != LWARX_INSTRUCTION
+      && (insn & LWARX_MASK) != LDARX_INSTRUCTION)
+    return 0;
 
-void
-rs6000_software_single_step (enum target_signal signal,
-                            int insert_breakpoints_p)
+  /* Assume that no atomic sequence is longer than "atomic_sequence_length" 
+     instructions.  */
+  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+    {
+      loc += PPC_INSN_SIZE;
+      insn = read_memory_integer (loc, PPC_INSN_SIZE);
+
+      /* Assume that there is at most one conditional branch in the atomic
+         sequence.  If a conditional branch is found, put a breakpoint in 
+         its destination address.  */
+      if ((insn & BC_MASK) == BC_INSTRUCTION)
+        {
+          if (bc_insn_count >= 1)
+            return 0; /* More than one conditional branch found, fallback 
+                         to the standard single-step code.  */
+          
+          opcode = insn >> 26;
+          branch_bp = branch_dest (opcode, insn, pc, breaks[0]);
+          
+          if (branch_bp != -1)
+            {
+              breaks[1] = branch_bp;
+              bc_insn_count++;
+              last_breakpoint++;
+            }
+        }
+
+      if ((insn & STWCX_MASK) == STWCX_INSTRUCTION
+          || (insn & STWCX_MASK) == STDCX_INSTRUCTION)
+        break;
+    }
+
+  /* Assume that the atomic sequence ends with a stwcx/stdcx instruction.  */
+  if ((insn & STWCX_MASK) != STWCX_INSTRUCTION
+      && (insn & STWCX_MASK) != STDCX_INSTRUCTION)
+    return 0;
+
+  closing_insn = loc;
+  loc += PPC_INSN_SIZE;
+  insn = read_memory_integer (loc, PPC_INSN_SIZE);
+
+  /* Insert a breakpoint right after the end of the atomic sequence.  */
+  breaks[0] = loc;
+
+  /* Check for duplicated breakpoints.  Check also for a breakpoint
+     placed (branch instruction's destination) at the stwcx/stdcx 
+     instruction, this resets the reservation and take us back to the 
+     lwarx/ldarx instruction at the beginning of the atomic sequence.  */
+  if (last_breakpoint && ((breaks[1] == breaks[0]) 
+      || (breaks[1] == closing_insn)))
+    last_breakpoint = 0;
+
+  /* Effectively inserts the breakpoints.  */
+  for (index = 0; index <= last_breakpoint; index++)
+    insert_single_step_breakpoint (breaks[index]);
+
+  return 1;
+}
+
+/* AIX does not support PT_STEP.  Simulate it.  */
+
+int
+rs6000_software_single_step (struct regcache *regcache)
 {
   CORE_ADDR dummy;
   int breakp_sz;
@@ -733,33 +817,32 @@ rs6000_software_single_step (enum target_signal signal,
   CORE_ADDR breaks[2];
   int opcode;
 
-  if (insert_breakpoints_p)
-    {
-      loc = read_pc ();
+  loc = read_pc ();
 
-      insn = read_memory_integer (loc, 4);
+  insn = read_memory_integer (loc, 4);
 
-      breaks[0] = loc + breakp_sz;
-      opcode = insn >> 26;
-      breaks[1] = branch_dest (opcode, insn, loc, breaks[0]);
+  if (deal_with_atomic_sequence (regcache))
+    return 1;
+  
+  breaks[0] = loc + breakp_sz;
+  opcode = insn >> 26;
+  breaks[1] = branch_dest (opcode, insn, loc, breaks[0]);
 
-      /* Don't put two breakpoints on the same address. */
-      if (breaks[1] == breaks[0])
-       breaks[1] = -1;
+  /* Don't put two breakpoints on the same address. */
+  if (breaks[1] == breaks[0])
+    breaks[1] = -1;
 
-      for (ii = 0; ii < 2; ++ii)
-       {
-         /* ignore invalid breakpoint. */
-         if (breaks[ii] == -1)
-           continue;
-         insert_single_step_breakpoint (breaks[ii]);
-       }
+  for (ii = 0; ii < 2; ++ii)
+    {
+      /* ignore invalid breakpoint. */
+      if (breaks[ii] == -1)
+       continue;
+      insert_single_step_breakpoint (breaks[ii]);
     }
-  else
-    remove_single_step_breakpoints ();
 
   errno = 0;                   /* FIXME, don't ignore errors! */
   /* What errors?  {read,write}_memory call error().  */
+  return 1;
 }
 
 
@@ -1510,7 +1593,7 @@ rs6000_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   struct value *arg = 0;
   struct type *type;
 
-  CORE_ADDR saved_sp;
+  ULONGEST saved_sp;
 
   /* The calling convention this function implements assumes the
      processor has floating-point registers.  We shouldn't be using it
@@ -1600,7 +1683,8 @@ rs6000_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
       else
        {
          /* Argument can fit in one register.  No problem.  */
-         int adj = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? reg_size - len : 0;
+         int adj = gdbarch_byte_order (current_gdbarch)
+                   == BFD_ENDIAN_BIG ? reg_size - len : 0;
          gdb_byte word[MAX_REGISTER_SIZE];
 
          memset (word, 0, reg_size);
@@ -1612,7 +1696,7 @@ rs6000_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
 
 ran_out_of_registers_for_arguments:
 
-  saved_sp = read_sp ();
+  regcache_cooked_read_unsigned (regcache, SP_REGNUM, &saved_sp);
 
   /* Location for 8 parameters are always reserved.  */
   sp -= wordsize * 8;
@@ -1719,7 +1803,7 @@ ran_out_of_registers_for_arguments:
       regcache_raw_write_signed (regcache, tdep->ppc_toc_regnum, tocvalue);
     }
 
-  target_store_registers (-1);
+  target_store_registers (regcache, -1);
   return sp;
 }
 
@@ -2008,8 +2092,8 @@ rs6000_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
   int vector_p;
   int general_p;
 
-  if (REGISTER_NAME (regnum) == NULL
-      || *REGISTER_NAME (regnum) == '\0')
+  if (gdbarch_register_name (current_gdbarch, regnum) == NULL
+      || *gdbarch_register_name (current_gdbarch, regnum) == '\0')
     return 0;
   if (group == all_reggroup)
     return 1;
@@ -2137,7 +2221,7 @@ e500_move_ev_register (void (*move) (struct regcache *regcache,
 
   reg_index = ev_reg - tdep->ppc_ev0_regnum;
 
-  if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+  if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
     {
       move (regcache, tdep->ppc_ev0_upper_regnum + reg_index, byte_buffer);
       move (regcache, tdep->ppc_gp0_regnum + reg_index, byte_buffer + 4);
@@ -2273,6 +2357,8 @@ rs6000_dwarf2_reg_to_regnum (int num)
   else
     switch (num)
       {
+      case 64:
+       return tdep->ppc_cr_regnum;
       case 67:
         return tdep->ppc_vrsave_regnum - 1; /* vscr */
       case 99:
@@ -2294,6 +2380,69 @@ rs6000_dwarf2_reg_to_regnum (int num)
       }
 }
 
+/* Translate a .eh_frame register to DWARF register, or adjust a
+   .debug_frame register.  */
+
+static int
+rs6000_adjust_frame_regnum (struct gdbarch *gdbarch, int num, int eh_frame_p)
+{
+  /* GCC releases before 3.4 use GCC internal register numbering in
+     .debug_frame (and .debug_info, et cetera).  The numbering is
+     different from the standard SysV numbering for everything except
+     for GPRs and FPRs.  We can not detect this problem in most cases
+     - to get accurate debug info for variables living in lr, ctr, v0,
+     et cetera, use a newer version of GCC.  But we must detect
+     one important case - lr is in column 65 in .debug_frame output,
+     instead of 108.
+
+     GCC 3.4, and the "hammer" branch, have a related problem.  They
+     record lr register saves in .debug_frame as 108, but still record
+     the return column as 65.  We fix that up too.
+
+     We can do this because 65 is assigned to fpsr, and GCC never
+     generates debug info referring to it.  To add support for
+     handwritten debug info that restores fpsr, we would need to add a
+     producer version check to this.  */
+  if (!eh_frame_p)
+    {
+      if (num == 65)
+       return 108;
+      else
+       return num;
+    }
+
+  /* .eh_frame is GCC specific.  For binary compatibility, it uses GCC
+     internal register numbering; translate that to the standard DWARF2
+     register numbering.  */
+  if (0 <= num && num <= 63)   /* r0-r31,fp0-fp31 */
+    return num;
+  else if (68 <= num && num <= 75) /* cr0-cr8 */
+    return num - 68 + 86;
+  else if (77 <= num && num <= 108) /* vr0-vr31 */
+    return num - 77 + 1124;
+  else
+    switch (num)
+      {
+      case 64: /* mq */
+       return 100;
+      case 65: /* lr */
+       return 108;
+      case 66: /* ctr */
+       return 109;
+      case 76: /* xer */
+       return 101;
+      case 109: /* vrsave */
+       return 356;
+      case 110: /* vscr */
+       return 67;
+      case 111: /* spe_acc */
+       return 99;
+      case 112: /* spefscr */
+       return 612;
+      default:
+       return num;
+      }
+}
 \f
 /* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG).
 
@@ -2326,7 +2475,7 @@ rs6000_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
     return addr;
 
   /* ADDR is in the data space, so it's a special function pointer. */
-  return read_memory_addr (addr, gdbarch_tdep (current_gdbarch)->wordsize);
+  return read_memory_addr (addr, gdbarch_tdep (gdbarch)->wordsize);
 }
 \f
 
@@ -2878,7 +3027,7 @@ gdb_print_insn_powerpc (bfd_vma memaddr, disassemble_info *info)
   if (!info->disassembler_options)
     info->disassembler_options = "any";
 
-  if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+  if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
     return print_insn_big_powerpc (memaddr, info);
   else
     return print_insn_little_powerpc (memaddr, info);
@@ -2962,7 +3111,7 @@ rs6000_frame_cache (struct frame_info *next_frame, void **this_cache)
       if (make_frame)
        {
          fdata.frameless = 0;
-         fdata.lr_offset = wordsize;
+         fdata.lr_offset = tdep->lr_frame_offset;
        }
     }
 
@@ -3400,6 +3549,9 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
   set_gdbarch_breakpoint_from_pc (gdbarch, rs6000_breakpoint_from_pc);
 
+  /* Handles single stepping of atomic sequences.  */
+  set_gdbarch_software_single_step (gdbarch, deal_with_atomic_sequence);
+  
   /* Handle the 64-bit SVR4 minimal-symbol convention of using "FN"
      for the descriptor and ".FN" for the entry-point -- a user
      specifying "break FN" will unexpectedly end up with a breakpoint
@@ -3428,6 +3580,10 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     (gdbarch, rs6000_in_solib_return_trampoline);
   set_gdbarch_skip_trampoline_code (gdbarch, rs6000_skip_trampoline_code);
 
+  /* Hook in the DWARF CFI frame unwinder.  */
+  frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+  dwarf2_frame_set_adjust_regnum (gdbarch, rs6000_adjust_frame_regnum);
+
   /* Hook in ABI-specific overrides, if they have been registered.  */
   gdbarch_init_osabi (info, gdbarch);