arc: Add prologue analysis
authorAnton Kolesov <Anton.Kolesov@synopsys.com>
Fri, 10 Feb 2017 11:12:09 +0000 (14:12 +0300)
committerAnton Kolesov <Anton.Kolesov@synopsys.com>
Tue, 28 Mar 2017 18:38:32 +0000 (21:38 +0300)
Add a prologue analysis that recognizes all instructions that may happen in
compiler-generated prologue, including various stores, core register moves,
subtraction and ENTER_S instruction that does a lot of prologue actions through
microcode.

Testcases cover various prologue scenarios, including instructions that are
spread across multiple 16-bit encodings (for example there are 7 encodings of
store instruction).

gdb/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

* arc-tdep.c (arc_frame_cache): Add support for prologue analysis.
(arc_skip_prologue): Likewise.
(arc_make_frame_cache): Likewise.
(arc_pv_get_operand): New function.
(arc_is_in_prologue): Likewise.
(arc_analyze_prologue): Likewise.
(arc_print_frame_cache): Likewise.
(MAX_PROLOGUE_LENGTH): New constant.

gdb/doc/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

* gdb.texinfo (Synopsys ARC): Document "set debug arc 2".

gdb/testsuite/ChangeLog:

yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>

* gdb.arch/arc-analyze-prologue.S: New file.
* gdb.arch/arc-analyze-prologue.exp: Likewise.

gdb/ChangeLog
gdb/arc-tdep.c
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/arc-analyze-prologue.S [new file with mode: 0644]
gdb/testsuite/gdb.arch/arc-analyze-prologue.exp [new file with mode: 0644]

index cb66e81..3520eec 100644 (file)
@@ -1,5 +1,16 @@
 2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
 
+       * arc-tdep.c (arc_frame_cache): Add support for prologue analysis.
+       (arc_skip_prologue): Likewise.
+       (arc_make_frame_cache): Likewise.
+       (arc_pv_get_operand): New function.
+       (arc_is_in_prologue): Likewise.
+       (arc_analyze_prologue): Likewise.
+       (arc_print_frame_cache): Likewise.
+       (MAX_PROLOGUE_LENGTH): New constant.
+
+2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
+
        * configure.tgt: Add arc-insn.o.
        * arc-tdep.c (arc_delayed_print_insn): Make non-static.
        (dump_arc_instruction_command): New function.
index 69ac641..657b1eb 100644 (file)
@@ -28,6 +28,7 @@
 #include "gdbcore.h"
 #include "gdbcmd.h"
 #include "objfiles.h"
+#include "prologue-value.h"
 #include "trad-frame.h"
 
 /* ARC header files.  */
@@ -42,8 +43,7 @@
 #include "features/arc-v2.c"
 #include "features/arc-arcompact.c"
 
-/* The frame unwind cache for the ARC.  Current structure is a stub, because
-   it should be filled in during the prologue analysis.  */
+/* The frame unwind cache for ARC.  */
 
 struct arc_frame_cache
 {
@@ -52,7 +52,35 @@ struct arc_frame_cache
      frame.  */
   CORE_ADDR prev_sp;
 
-  /* Store addresses for registers saved in prologue.  */
+  /* Register that is a base for this frame - FP for normal frame, SP for
+     non-FP frames.  */
+  int frame_base_reg;
+
+  /* Offset from the previous SP to the current frame base.  If GCC uses
+     `SUB SP,SP,offset` to allocate space for local variables, then it will be
+     done after setting up a frame pointer, but it still will be considered
+     part of prologue, therefore SP will be lesser than FP at the end of the
+     prologue analysis.  In this case that would be an offset from old SP to a
+     new FP.  But in case of non-FP frames, frame base is an SP and thus that
+     would be an offset from old SP to new SP.  What is important is that this
+     is an offset from old SP to a known register, so it can be used to find
+     old SP.
+
+     Using FP is preferable, when possible, because SP can change in function
+     body after prologue due to alloca, variadic arguments or other shenanigans.
+     If that is the case in the caller frame, then PREV_SP will point to SP at
+     the moment of function call, but it will be different from SP value at the
+     end of the caller prologue.  As a result it will not be possible to
+     reconstruct caller's frame and go past it in the backtrace.  Those things
+     are unlikely to happen to FP - FP value at the moment of function call (as
+     stored on stack in callee prologue) is also an FP value at the end of the
+     caller's prologue.  */
+
+  LONGEST frame_base_offset;
+
+  /* Store addresses for registers saved in prologue.  During prologue analysis
+     GDB stores offsets relatively to "old SP", then after old SP is evaluated,
+     offsets are replaced with absolute addresses.  */
   struct trad_frame_saved_reg *saved_regs;
 };
 
@@ -117,6 +145,10 @@ static const char *const core_arcompact_register_names[] = {
   "lp_count", "reserved", "limm", "pcl",
 };
 
+/* Functions are sorted in the order as they are used in the
+   _initialize_arc_tdep (), which uses the same order as gdbarch.h.  Static
+   functions are defined before the first invocation.  */
+
 /* Returns an unsigned value of OPERAND_NUM in instruction INSN.
    For relative branch instructions returned value is an offset, not an actual
    branch target.  */
@@ -227,10 +259,6 @@ arc_insn_get_memory_offset (const struct arc_instruction &insn)
   return value;
 }
 
-/* Functions are sorted in the order as they are used in the
-   _initialize_arc_tdep (), which uses the same order as gdbarch.h.  Static
-   functions are defined before the first invocation.  */
-
 CORE_ADDR
 arc_insn_get_branch_target (const struct arc_instruction &insn)
 {
@@ -913,6 +941,247 @@ arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache)
   return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM);
 }
 
+/* Helper function that returns valid pv_t for an instruction operand:
+   either a register or a constant.  */
+
+static pv_t
+arc_pv_get_operand (pv_t *regs, const struct arc_instruction &insn, int operand)
+{
+  if (insn.operands[operand].kind == ARC_OPERAND_KIND_REG)
+    return regs[insn.operands[operand].value];
+  else
+    return pv_constant (arc_insn_get_operand_value (insn, operand));
+}
+
+/* Determine whether the given disassembled instruction may be part of a
+   function prologue.  If it is, the information in the frame unwind cache will
+   be updated.  */
+
+static bool
+arc_is_in_prologue (struct gdbarch *gdbarch, const struct arc_instruction &insn,
+                   pv_t *regs, struct pv_area *stack)
+{
+  /* It might be that currently analyzed address doesn't contain an
+     instruction, hence INSN is not valid.  It likely means that address points
+     to a data, non-initialized memory, or middle of a 32-bit instruction.  In
+     practice this may happen if GDB connects to a remote target that has
+     non-zeroed memory.  GDB would read PC value and would try to analyze
+     prologue, but there is no guarantee that memory contents at the address
+     specified in PC is address is a valid instruction.  There is not much that
+     that can be done about that.  */
+  if (!insn.valid)
+    return false;
+
+  /* Branch/jump or a predicated instruction.  */
+  if (insn.is_control_flow || insn.condition_code != ARC_CC_AL)
+    return false;
+
+  /* Store of some register.  May or may not update base address register.  */
+  if (insn.insn_class == STORE || insn.insn_class == PUSH)
+    {
+      /* There is definetely at least one operand - register/value being
+        stored.  */
+      gdb_assert (insn.operands_count > 0);
+
+      /* Store at some constant address.  */
+      if (insn.operands_count > 1
+         && insn.operands[1].kind != ARC_OPERAND_KIND_REG)
+       return false;
+
+      /* Writeback modes:
+        Mode   Address used                Writeback value
+        --------------------------------------------------
+        No     reg + offset                no
+        A/AW   reg + offset                reg + offset
+        AB     reg                         reg + offset
+        AS     reg + (offset << scaling)   no
+
+        "PUSH reg" is an alias to "ST.AW reg, [SP, -4]" encoding.  However
+        16-bit PUSH_S is a distinct instruction encoding, where offset and
+        base register are implied through opcode.  */
+
+      /* Register with base memory address.  */
+      int base_reg = arc_insn_get_memory_base_reg (insn);
+
+      /* Address where to write.  arc_insn_get_memory_offset returns scaled
+        value for ARC_WRITEBACK_AS.  */
+      pv_t addr;
+      if (insn.writeback_mode == ARC_WRITEBACK_AB)
+       addr = regs[base_reg];
+      else
+       addr = pv_add_constant (regs[base_reg],
+                               arc_insn_get_memory_offset (insn));
+
+      if (pv_area_store_would_trash (stack, addr))
+       return false;
+
+      if (insn.data_size_mode != ARC_SCALING_D)
+       {
+         /* Find the value being stored.  */
+         pv_t store_value = arc_pv_get_operand (regs, insn, 0);
+
+         /* What is the size of a the stored value?  */
+         CORE_ADDR size;
+         if (insn.data_size_mode == ARC_SCALING_B)
+           size = 1;
+         else if (insn.data_size_mode == ARC_SCALING_H)
+           size = 2;
+         else
+           size = ARC_REGISTER_SIZE;
+
+         pv_area_store (stack, addr, size, store_value);
+       }
+      else
+       {
+         if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+           {
+             /* If this is a double store, than write N+1 register as well.  */
+             pv_t store_value1 = regs[insn.operands[0].value];
+             pv_t store_value2 = regs[insn.operands[0].value + 1];
+             pv_area_store (stack, addr, ARC_REGISTER_SIZE, store_value1);
+             pv_area_store (stack,
+                            pv_add_constant (addr, ARC_REGISTER_SIZE),
+                            ARC_REGISTER_SIZE, store_value2);
+           }
+         else
+           {
+             pv_t store_value
+               = pv_constant (arc_insn_get_operand_value (insn, 0));
+             pv_area_store (stack, addr, ARC_REGISTER_SIZE * 2, store_value);
+           }
+       }
+
+      /* Is base register updated?  */
+      if (insn.writeback_mode == ARC_WRITEBACK_A
+         || insn.writeback_mode == ARC_WRITEBACK_AB)
+       regs[base_reg] = pv_add_constant (regs[base_reg],
+                                         arc_insn_get_memory_offset (insn));
+
+      return true;
+    }
+  else if (insn.insn_class == MOVE)
+    {
+      gdb_assert (insn.operands_count == 2);
+
+      /* Destination argument can be "0", so nothing will happen.  */
+      if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+       {
+         int dst_regnum = insn.operands[0].value;
+         regs[dst_regnum] = arc_pv_get_operand (regs, insn, 1);
+       }
+      return true;
+    }
+  else if (insn.insn_class == SUB)
+    {
+      gdb_assert (insn.operands_count == 3);
+
+      /* SUB 0,b,c.  */
+      if (insn.operands[0].kind != ARC_OPERAND_KIND_REG)
+       return true;
+
+      int dst_regnum = insn.operands[0].value;
+      regs[dst_regnum] = pv_subtract (arc_pv_get_operand (regs, insn, 1),
+                                     arc_pv_get_operand (regs, insn, 2));
+      return true;
+    }
+  else if (insn.insn_class == ENTER)
+    {
+      /* ENTER_S is a prologue-in-instruction - it saves all callee-saved
+        registers according to given arguments thus greatly reducing code
+        size.  Which registers will be actually saved depends on arguments.
+
+        ENTER_S {R13-...,FP,BLINK} stores registers in following order:
+
+        new SP ->
+                  BLINK
+                  R13
+                  R14
+                  R15
+                  ...
+                  FP
+        old SP ->
+
+        There are up to three arguments for this opcode, as presented by ARC
+        disassembler:
+        1) amount of general-purpose registers to be saved - this argument is
+           always present even when it is 0;
+        2) FP register number (27) if FP has to be stored, otherwise argument
+           is not present;
+        3) BLINK register number (31) if BLINK has to be stored, otherwise
+           argument is not present.  If both FP and BLINK are stored, then FP
+           is present before BLINK in argument list.  */
+      gdb_assert (insn.operands_count > 0);
+
+      int regs_saved = arc_insn_get_operand_value (insn, 0);
+
+      bool is_fp_saved;
+      if (insn.operands_count > 1)
+       is_fp_saved = (insn.operands[1].value  == ARC_FP_REGNUM);
+      else
+       is_fp_saved = false;
+
+      bool is_blink_saved;
+      if (insn.operands_count > 1)
+       is_blink_saved = (insn.operands[insn.operands_count - 1].value
+                         == ARC_BLINK_REGNUM);
+      else
+       is_blink_saved = false;
+
+      /* Amount of bytes to be allocated to store specified registers.  */
+      CORE_ADDR st_size = ((regs_saved + is_fp_saved + is_blink_saved)
+                          * ARC_REGISTER_SIZE);
+      pv_t new_sp = pv_add_constant (regs[ARC_SP_REGNUM], -st_size);
+
+      /* Assume that if the last register (closest to new SP) can be written,
+        then it is possible to write all of them.  */
+      if (pv_area_store_would_trash (stack, new_sp))
+       return false;
+
+      /* Current store address.  */
+      pv_t addr = regs[ARC_SP_REGNUM];
+
+      if (is_fp_saved)
+       {
+         addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+         pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[ARC_FP_REGNUM]);
+       }
+
+      /* Registers are stored in backward order: from GP (R26) to R13.  */
+      for (int i = ARC_R13_REGNUM + regs_saved - 1; i >= ARC_R13_REGNUM; i--)
+       {
+         addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+         pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[i]);
+       }
+
+      if (is_blink_saved)
+       {
+         addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+         pv_area_store (stack, addr, ARC_REGISTER_SIZE,
+                        regs[ARC_BLINK_REGNUM]);
+       }
+
+      gdb_assert (pv_is_identical (addr, new_sp));
+
+      regs[ARC_SP_REGNUM] = new_sp;
+
+      if (is_fp_saved)
+       regs[ARC_FP_REGNUM] = regs[ARC_SP_REGNUM];
+
+      return true;
+    }
+
+  /* Some other architectures, like nds32 or arm, try to continue as far as
+     possible when building a prologue cache (as opposed to when skipping
+     prologue), so that cache will be as full as possible.  However current
+     code for ARC doesn't recognize some instructions that may modify SP, like
+     ADD, AND, OR, etc, hence there is no way to guarantee that SP wasn't
+     clobbered by the skipped instruction.  Potential existence of extension
+     instruction, which may do anything they want makes this even more complex,
+     so it is just better to halt on a first unrecognized instruction.  */
+
+  return false;
+}
+
 /* Copy of gdb_buffered_insn_length_fprintf from disasm.c.  */
 
 static int ATTRIBUTE_PRINTF (2, 3)
@@ -937,6 +1206,146 @@ arc_disassemble_info (struct gdbarch *gdbarch)
   return di;
 }
 
+/* Analyze the prologue and update the corresponding frame cache for the frame
+   unwinder for unwinding frames that doesn't have debug info.  In such
+   situation GDB attempts to parse instructions in the prologue to understand
+   where each register is saved.
+
+   If CACHE is not NULL, then it will be filled with information about saved
+   registers.
+
+   There are several variations of prologue which GDB may encouter.  "Full"
+   prologue looks like this:
+
+       sub     sp,sp,<imm>   ; Space for variadic arguments.
+       push    blink         ; Store return address.
+       push    r13           ; Store callee saved registers (up to R26/GP).
+       push    r14
+       push    fp            ; Store frame pointer.
+       mov     fp,sp         ; Update frame pointer.
+       sub     sp,sp,<imm>   ; Create space for local vars on the stack.
+
+   Depending on compiler options lots of things may change:
+
+    1) BLINK is not saved in leaf functions.
+    2) Frame pointer is not saved and updated if -fomit-frame-pointer is used.
+    3) 16-bit versions of those instructions may be used.
+    4) Instead of a sequence of several push'es, compiler may instead prefer to
+    do one subtract on stack pointer and then store registers using normal
+    store, that doesn't update SP.  Like this:
+
+
+       sub     sp,sp,8         ; Create space for calee-saved registers.
+       st      r13,[sp,4]      ; Store callee saved registers (up to R26/GP).
+       st      r14,[sp,0]
+
+    5) ENTER_S instruction can encode most of prologue sequence in one
+    instruction (except for those subtracts for variadic arguments and local
+    variables).
+    6) GCC may use "millicode" functions from libgcc to store callee-saved
+    registers with minimal code-size requirements.  This function currently
+    doesn't support this.
+
+   ENTRYPOINT is a function entry point where prologue starts.
+
+   LIMIT_PC is a maximum possible end address of prologue (meaning address
+   of first instruction after the prologue).  It might also point to the middle
+   of prologue if execution has been stopped by the breakpoint at this address
+   - in this case debugger should analyze prologue only up to this address,
+   because further instructions haven't been executed yet.
+
+   Returns address of the first instruction after the prologue.  */
+
+static CORE_ADDR
+arc_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR entrypoint,
+                     const CORE_ADDR limit_pc, struct arc_frame_cache *cache)
+{
+  if (arc_debug)
+    debug_printf ("arc: analyze_prologue (entrypoint=%s, limit_pc=%s)\n",
+                 paddress (gdbarch, entrypoint),
+                 paddress (gdbarch, limit_pc));
+
+  /* Prologue values.  Only core registers can be stored.  */
+  pv_t regs[ARC_LAST_CORE_REGNUM + 1];
+  for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+    regs[i] = pv_register (i, 0);
+  struct pv_area *stack = make_pv_area (ARC_SP_REGNUM,
+                                       gdbarch_addr_bit (gdbarch));
+  struct cleanup *back_to = make_cleanup_free_pv_area (stack);
+
+  CORE_ADDR current_prologue_end = entrypoint;
+
+  /* Look at each instruction in the prologue.  */
+  while (current_prologue_end < limit_pc)
+    {
+      struct arc_instruction insn;
+      struct disassemble_info di = arc_disassemble_info (gdbarch);
+      arc_insn_decode (current_prologue_end, &di, arc_delayed_print_insn,
+                      &insn);
+
+      if (arc_debug >= 2)
+       arc_insn_dump (insn);
+
+      /* If this instruction is in the prologue, fields in the cache will be
+        updated, and the saved registers mask may be updated.  */
+      if (!arc_is_in_prologue (gdbarch, insn, regs, stack))
+       {
+         /* Found an instruction that is not in the prologue.  */
+         if (arc_debug)
+           debug_printf ("arc: End of prologue reached at address %s\n",
+                         paddress (gdbarch, insn.address));
+         break;
+       }
+
+      current_prologue_end = arc_insn_get_linear_next_pc (insn);
+    }
+
+  if (cache != NULL)
+    {
+      /* Figure out if it is a frame pointer or just a stack pointer.  */
+      if (pv_is_register (regs[ARC_FP_REGNUM], ARC_SP_REGNUM))
+       {
+         cache->frame_base_reg = ARC_FP_REGNUM;
+         cache->frame_base_offset = -regs[ARC_FP_REGNUM].k;
+       }
+      else
+       {
+         cache->frame_base_reg = ARC_SP_REGNUM;
+         cache->frame_base_offset = -regs[ARC_SP_REGNUM].k;
+       }
+
+      /* Assign offset from old SP to all saved registers.  */
+      for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+       {
+         CORE_ADDR offset;
+         if (pv_area_find_reg (stack, gdbarch, i, &offset))
+           cache->saved_regs[i].addr = offset;
+       }
+    }
+
+  do_cleanups (back_to);
+  return current_prologue_end;
+}
+
+/* Estimated maximum prologue length in bytes.  This should include:
+   1) Store instruction for each callee-saved register (R25 - R13 + 1)
+   2) Two instructions for FP
+   3) One for BLINK
+   4) Three substract instructions for SP (for variadic args, for
+   callee saved regs and for local vars) and assuming that those SUB use
+   long-immediate (hence double length).
+   5) Stores of arguments registers are considered part of prologue too
+      (R7 - R1 + 1).
+   This is quite an extreme case, because even with -O0 GCC will collapse first
+   two SUBs into one and long immediate values are quite unlikely to appear in
+   this case, but still better to overshoot a bit - prologue analysis will
+   anyway stop at the first instruction that doesn't fit prologue, so this
+   limit will be rarely reached.  */
+
+const static int MAX_PROLOGUE_LENGTH
+  = 4 * (ARC_R25_REGNUM - ARC_R13_REGNUM + 1 + 2 + 1 + 6
+        + ARC_LAST_ARG_REGNUM - ARC_FIRST_ARG_REGNUM + 1);
+
 /* Implement the "skip_prologue" gdbarch method.
 
    Skip the prologue for the function at PC.  This is done by checking from
@@ -966,15 +1375,19 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
   /* No prologue info in symbol table, have to analyze prologue.  */
 
   /* Find an upper limit on the function prologue using the debug
-     information.  If the debug information could not be used to provide that
-     bound, then pass 0 and arc_scan_prologue will estimate value itself.  */
+     information.  If there is no debug information about prologue end, then
+     skip_prologue_using_sal will return 0.  */
   CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc);
-  /* We don't have a proper analyze_prologue function yet, but its result
-     should be returned here.  Currently GDB will just stop at the first
-     instruction of function if debug information doesn't have prologue info;
-     and if there is a debug info about prologue - this code path will not be
-     taken at all.  */
-  return (limit_pc == 0 ? pc : limit_pc);
+
+  /* If there is no debug information at all, it is required to give some
+     semi-arbitrary hard limit on amount of bytes to scan during prologue
+     analysis.  */
+  if (limit_pc == 0)
+    limit_pc = pc + MAX_PROLOGUE_LENGTH;
+
+  /* Find the address of the first instruction after the prologue by scanning
+     through it - no other information is needed, so pass NULL as a cache.  */
+  return arc_analyze_prologue (gdbarch, pc, limit_pc, NULL);
 }
 
 /* Implement the "print_insn" gdbarch method.
@@ -1114,6 +1527,28 @@ arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
   return align_down (sp, 4);
 }
 
+/* Dump the frame info.  Used for internal debugging only.  */
+
+static void
+arc_print_frame_cache (struct gdbarch *gdbarch, char *message,
+                      struct arc_frame_cache *cache, int addresses_known)
+{
+  debug_printf ("arc: frame_info %s\n", message);
+  debug_printf ("arc: prev_sp = %s\n", paddress (gdbarch, cache->prev_sp));
+  debug_printf ("arc: frame_base_reg = %i\n", cache->frame_base_reg);
+  debug_printf ("arc: frame_base_offset = %s\n",
+               plongest (cache->frame_base_offset));
+
+  for (int i = 0; i <= ARC_BLINK_REGNUM; i++)
+    {
+      if (trad_frame_addr_p (cache->saved_regs, i))
+       debug_printf ("arc: saved register %s at %s %s\n",
+                     gdbarch_register_name (gdbarch, i),
+                     (addresses_known) ? "address" : "offset",
+                     paddress (gdbarch, cache->saved_regs[i].addr));
+    }
+}
+
 /* Frame unwinder for normal frames.  */
 
 static struct arc_frame_cache *
@@ -1125,12 +1560,11 @@ arc_make_frame_cache (struct frame_info *this_frame)
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
 
   CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
-  CORE_ADDR prev_pc = get_frame_pc (this_frame);
-
   CORE_ADDR entrypoint, prologue_end;
   if (find_pc_partial_function (block_addr, NULL, &entrypoint, &prologue_end))
     {
       struct symtab_and_line sal = find_pc_line (entrypoint, 0);
+      CORE_ADDR prev_pc = get_frame_pc (this_frame);
       if (sal.line == 0)
        /* No line info so use current PC.  */
        prologue_end = prev_pc;
@@ -1142,18 +1576,42 @@ arc_make_frame_cache (struct frame_info *this_frame)
     }
   else
     {
+      /* If find_pc_partial_function returned nothing then there is no symbol
+        information at all for this PC.  Currently it is assumed in this case
+        that current PC is entrypoint to function and try to construct the
+        frame from that.  This is, probably, suboptimal, for example ARM
+        assumes in this case that program is inside the normal frame (with
+        frame pointer).  ARC, perhaps, should try to do the same.  */
       entrypoint = get_frame_register_unsigned (this_frame,
                                                gdbarch_pc_regnum (gdbarch));
-      prologue_end = 0;
+      prologue_end = entrypoint + MAX_PROLOGUE_LENGTH;
     }
 
   /* Allocate new frame cache instance and space for saved register info.
-   * FRAME_OBSTACK_ZALLOC will initialize fields to zeroes.  */
+     FRAME_OBSTACK_ZALLOC will initialize fields to zeroes.  */
   struct arc_frame_cache *cache
     = FRAME_OBSTACK_ZALLOC (struct arc_frame_cache);
   cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
 
-  /* Should call analyze_prologue here, when it will be implemented.  */
+  arc_analyze_prologue (gdbarch, entrypoint, prologue_end, cache);
+
+  if (arc_debug)
+    arc_print_frame_cache (gdbarch, "after prologue", cache, false);
+
+  CORE_ADDR unwound_fb = get_frame_register_unsigned (this_frame,
+                                                     cache->frame_base_reg);
+  if (unwound_fb == 0)
+    return cache;
+  cache->prev_sp = unwound_fb + cache->frame_base_offset;
+
+  for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+    {
+      if (trad_frame_addr_p (cache->saved_regs, i))
+       cache->saved_regs[i].addr += cache->prev_sp;
+    }
+
+  if (arc_debug)
+    arc_print_frame_cache (gdbarch, "after previous SP found", cache, true);
 
   return cache;
 }
index 3088f13..c542c0b 100644 (file)
@@ -1,5 +1,9 @@
 2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
 
+       * gdb.texinfo (Synopsys ARC): Document "set debug arc 2".
+
+2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
+
        * gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".
 
 2017-03-22  Yao Qi  <yao.qi@linaro.org>
index 7e2fc81..300d78e 100644 (file)
@@ -22098,8 +22098,7 @@ acceptable commands.
 @item set debug arc
 @kindex set debug arc
 Control the level of ARC specific debug messages.  Use 0 for no messages (the
-default) and 1 for debug messages.  At present higher values offer no further
-messages.
+default), 1 for debug messages, and 2 for even more debug messages.
 
 @item show debug arc
 @kindex show debug arc
index efccd01..5241a27 100644 (file)
@@ -1,5 +1,10 @@
 2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
 
+       * gdb.arch/arc-analyze-prologue.S: New file.
+       * gdb.arch/arc-analyze-prologue.exp: Likewise.
+
+2017-03-28  Anton Kolesov  <anton.kolesov@synopsys.com>
+
        * gdb.arch/arc-decode-insn.S: New file.
        * gdb.arch/arc-decode-insn.exp: Likewise.
 
diff --git a/gdb/testsuite/gdb.arch/arc-analyze-prologue.S b/gdb/testsuite/gdb.arch/arc-analyze-prologue.S
new file mode 100644 (file)
index 0000000..b32897a
--- /dev/null
@@ -0,0 +1,903 @@
+; This testcase is part of GDB, the GNU debugger.
+
+; Copyright 2017 Free Software Foundation, Inc.
+
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+.section .data
+some_variable:
+.long  0xdeadbeef
+
+.section .text
+.global        main
+.type  main, @function
+
+; Standard prologue.
+
+.align 4
+standard_prologue:
+       push    blink
+       sub     sp,sp,12
+       st      r13, [sp, 0]
+       st      r14, [sp, 4]
+       st      r18, [sp, 8]
+       add     r0, r1, r2
+       ld      r18, [sp, 8]
+       ld      r14, [sp, 4]
+       ld      r13, [sp, 0]
+       add     sp,sp,12
+       pop     blink
+       j       [blink]
+
+; Standard prologue using short instructions.
+
+.align 4
+mini_prologue:
+       push_s  blink
+       sub_s   sp,sp,12
+       ; ST_S can store only some of the core registers.
+       st_s    r13, [sp, 0]
+       st_s    r15, [sp, 4]
+       st_s    r14, [sp, 8]
+       add     r0, r1, r2
+       add     sp,sp,16
+       j       [blink]
+
+; Standard prologue without `sub sp,sp,INTEGER`.
+
+.align 4
+no_subsp_prologue:
+       push    blink
+       push    r13
+       push    r20
+       push    r25
+       add     r0, r1, r2
+       pop     r25
+       pop     r20
+       pop     r13
+       pop     blink
+       j       [blink]
+
+; Standard prologue of leaf function.
+
+.align 4
+leaf_prologue:
+       sub     sp,sp,8
+       st      r13, [sp, 0]
+       st      r15, [sp, 4]
+       add     r0, r1, r2
+       ld      r13, [sp, 0]
+       ld      r15, [sp, 4]
+       j.d     [blink]
+       add     sp,sp,8
+
+; Prologue with `push fp`.
+
+.align 4
+pushfp_prologue:
+       push    r13
+       push    r14
+       push    fp
+       ; mov fp,sp is part of prologue, but this test will not verify that.
+       ; It will be checked later in the "arg_regs_fp" test.
+       mov     fp, sp
+       add     r0, r1, r2
+       pop     fp
+       pop     r14
+       pop     r13
+       j       [blink]
+
+; Prologue with frame pointer and store relative to FP.
+
+.align 4
+fp_prologue_with_store:
+       push    r13
+       push    r14
+       push    fp
+       mov     fp, sp
+       sub_s   sp,sp,4
+       st      r15,[fp,-4]
+       add     r0, r1, r2
+       pop     r15
+       pop     fp
+       pop     r14
+       pop     r13
+       j       [blink]
+
+; Verify that store of the non-callee saved registers is not part of prologue.
+; Repeat this test for multiple registers, to check boundaries.  Also check
+; with both ST and PUSH (aka ST.AW). We have to use multiple functions for
+; this, because GDB would stop analisys at the first instruction that is not
+; part of prologue.
+
+.align 4
+noncallee_saved_regs_r12_st:
+       sub     sp,sp,8
+       st      r13, [sp, 4]
+       st      r12, [sp, 0]
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+noncallee_saved_regs_r12_push:
+       push    r13
+       push    r12
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+noncallee_saved_regs_r2_push:
+       push    r13
+       push    r2
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+noncallee_saved_regs_gp_push:
+       push    r25
+       push    gp
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,8
+
+; LP_COUNT is treated like a normal register.
+
+.align 4
+noncallee_saved_regs_lp_count:
+       push    r25
+       push    lp_count
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,8
+
+; BLINK is saved, but after an instruction that is not part of prologue.
+; Currently arc_analyze_prologue stops analisys at the first intstruction
+; that is not a part of prologue.  This might be not the best way, but it is
+; what it is right now, so this test confirms this.
+
+.align 4
+noncallee_saved_regs_blink_out_of_prologue:
+       push    r25
+       push    gp
+       push    blink
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,12
+
+; Saving arguments register via FP.
+
+.align 4
+arg_regs_fp:
+       push    fp
+       mov     fp, sp
+       sub     sp, sp, 16
+       st      r0, [fp, -4]
+       st      r1, [fp, -8]
+       st      r7, [fp, -12]
+       st      r8, [fp, -16]
+       add     r0, r1, r2
+       add     sp,sp,16
+       pop     fp
+       j       [blink]
+
+; Like the previous, but with mov_s.
+
+.align 4
+arg_regs_fp_mov_s:
+       push    fp
+       mov_s   fp, sp
+       sub     sp, sp, 8
+       st      r0, [fp, -4]
+       ; Not part of the prologue.
+       st      r8, [fp, -8]
+       add     r0, r1, r2
+       add     sp,sp,8
+       pop     fp
+       j       [blink]
+
+; Saving arguments register without FP.
+
+.align 4
+arg_regs_sp:
+       sub     sp, sp, 24
+       st      r0, [sp, 0]
+       st      r1, [sp, 4]
+       st      r7, [sp, 8]
+       ; Normally that would be done before saving args, but it is used as a
+       ; marker that saving arguments relatively to SP is considered part of
+       ; prologue.
+       st      r13, [sp, 16]
+       ; Not part of the prologue.
+       st      r8, [sp, 12]
+       st      r14, [sp, 20]
+       add     r0, r1, r2
+       j.d     [blink]
+       add     sp,sp,24
+
+; ENTER_S that does nothing.
+
+.align 4
+enter_s_nop:
+       ; Effectively a nop.
+       enter_s 0
+       add     r0,r1,r2
+       j       [blink]
+
+; ENTER_S that stores BLINK.
+
+.align 4
+enter_s_blink:
+       enter_s 32
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,4
+
+; ENTER_S that stores FP.
+
+.align 4
+enter_s_fp:
+       enter_s 16
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,4
+
+; ENTER_S that stores R13, FP and BLINK.
+
+.align 4
+enter_s_r13:
+       enter_s (32 + 16 + 1)
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,12
+
+; ENTER_S that stores R13-R15
+
+.align 4
+enter_s_r15:
+       enter_s 3
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,12
+
+; ENTER_S that stores everything it could.
+
+.align 4
+enter_s_all:
+       enter_s (32 + 16 + 14)
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,64
+
+; Deeper nesting.
+
+.align 4
+nested_prologue_inner:
+       sub     sp,sp,8
+       st      r18, [sp, 4]
+       st      r13, [sp, 0]
+       add     r0, r1, r2
+       ld      r18, [sp, 4]
+       ld      r13, [sp, 0]
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+nested_prologue_outer:
+       push    blink
+       sub     sp,sp,8
+       st      r14, [sp, 0]
+       st      r15, [sp, 4]
+       bl      @nested_prologue_inner
+       add     r0, r1, r2
+       ld      r14, [sp, 0]
+       ld      r15, [sp, 4]
+       add     sp,sp,8
+       pop     blink
+       j       [blink]
+
+; Prologue with maximum length.
+; Expressions like (0xFFFFFFFF + 25) force assembler to use long immediate
+; even for values that don't need it, thus letting us test maksimum prologue
+; length without having huge frames.
+.align 4
+max_length_prologue:
+       ; Variadic args
+       sub     sp,sp,(0xFFFFFFFF + 25) ; 24 bytes
+       push    blink
+       ; Allocate space for 13 callee-saved and 8 arg regs.
+       sub     sp,sp,(0xFFFFFFFF + 1 + 21 * 4)
+       st      r13, [sp, 0]
+       st      r14, [sp, 4]
+       st      r15, [sp, 8]
+       st      r16, [sp, 12]
+       st      r17, [sp, 16]
+       st      r18, [sp, 20]
+       st      r19, [sp, 24]
+       st      r20, [sp, 28]
+       st      r21, [sp, 32]
+       st      r22, [sp, 36]
+       st      r23, [sp, 40]
+       st      r24, [sp, 44]
+       st      r25, [sp, 48]
+       st      r0,  [sp, 52]
+       st      r1,  [sp, 56]
+       st      r2,  [sp, 60]
+       st      r3,  [sp, 64]
+       st      r4,  [sp, 68]
+       st      r5,  [sp, 72]
+       st      r6,  [sp, 76]
+       st      r7,  [sp, 80]
+       push    fp
+       mov     fp,sp
+       sub     sp,sp,(0xFFFFFFFF + 1 + 16) ; Space for local variables.
+       ; End of prologue.
+       add     sp,sp,24 + 21 * 4 + 16
+       j       [blink]
+
+; Few tests that test that prologue analysis stops at branch.  There are four
+; types of "branches": conditional and non-conditional, relative branches and
+; absolute jumps.
+
+.align 4
+branch_in_prologue:
+       push    r13
+       b       @.L1
+       ; This store on stack is not a prologue.
+       push    r14
+.L1:
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,4
+
+.align 4
+cond_branch_in_prologue:
+       sub_s   sp,sp,8
+       st_s    r13,[sp,4]
+       ; Doesn't matter if branch is taken or not.
+       breq    r0,r1,@.L2
+       ; This store on stack is not a prologue.
+       st_s    r14,[sp,0]
+.L2:
+       add     r0,r1,r2
+       pop     fp
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+jump_in_prologue:
+       push    r13
+       j       @.L3
+       ; This store on stack is not a prologue.
+       push    r14
+.L3:
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,4
+
+.align 4
+cond_jump_in_prologue:
+       sub_s   sp,sp,8
+       st_s    r13,[sp,4]
+       ; It doesn't matter if jump is taken or not - prologue analysis has to
+       ; stop before `jeq` in any case.
+       jeq     @.L4
+       ; This store on stack is not a prologue.
+       st_s    r14,[sp,0]
+.L4:
+       add     r0,r1,r2
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+predicated_insn:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       st_s    r15,[sp,0]
+       ; Use SUB SP,SP,0 because it is otherwise a valid instruction for
+       ; prologue, so it will halt analysis purely because of its predicate.
+       sub.eq  sp,sp,0 ; This is not a prologue anymore.
+       st_s    r14,[sp,4]
+       add     sp,sp,12
+       j       [blink]
+
+; Loops should halt prologue analysis.
+
+.align 4
+loop_in_prologue:
+       push r25
+       push lp_count
+       mov lp_count, 4
+       lp @.Lloop_end1
+       push r26 ; Not part of prologue.
+       add     r0, r1, r2
+.Lloop_end1:
+       add     r1, r1, r2
+       pop r26
+       add sp,sp,8
+       pop r25
+       j   [blink]
+
+; Store of a constant value (not a register).
+
+.align 4
+store_constant:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       st      0xdeadbeef,[sp,0]
+       st_s    r14,[sp,4]
+       add     sp,sp,12
+       j       [blink]
+
+; Test that store to immediate address halts prologue analysis.
+.align 4
+st_c_limm:
+       push    r15
+       st      r14,[@some_variable]
+       push    r13
+       add     sp,sp,8
+       j       [blink]
+
+; Store with AB writeback mode.
+
+.align 4
+st_ab_writeback:
+       sub     sp,sp,8
+       st      r13,[sp,4]
+       st.ab   r14,[sp,-4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of a word with AS writeback mode.
+
+.align 4
+st_as_writeback:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       st.as   r14,[sp,1] ; ST.AS, hence address is (offset << 2).
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of a halfword with AS writeback mode.
+
+.align 4
+sth_as_writeback:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       sth.as  r14,[sp,2] ; STH.AS, hence address is (offset << 1).
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of a double word with AS writeback mode.  Shift is still 2, like ST!
+
+.align 4
+std_as_writeback:
+       sub     sp,sp,16
+       st      r13,[sp,12]
+#ifdef __ARC_LL64__
+       std.as  r14,[sp,1] ; STD.AS, hence address is (offset << 2).
+#else
+       st.as   r14,[sp,1] ; STD.AS, hence address is (offset << 2).
+       st.as   r15,[sp,2] ; STD.AS, hence address is (offset << 2).
+#endif
+       st      r16,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of the halfword.  R14 will not be reported as "saved".
+
+.align 4
+st_halfword:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       sth     r14,[sp,4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of the halfword.  R14 will not be reported as "saved".
+
+.align 4
+sts_halfword:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       mov     r13,sp
+       sth_s   r14,[r13,4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of the byte.  R14 will not be reported as "saved".
+
+.align 4
+st_byte:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       stb     r14,[sp,4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of the byte.  R14 will not be reported as "saved".
+
+.align 4
+sts_byte:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       mov     r13,sp
+       stb_s   r14,[r13,4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store of the byte.  R14 will not be reported as "saved".
+
+.align 4
+sts_byte_sp:
+       sub     sp,sp,12
+       st      r13,[sp,8]
+       stb_s   r14,[sp,4]
+       st      r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Double word store, optionally available for ARC HS.
+
+.align 4
+st_double:
+       sub     sp,sp,8
+#ifdef __ARC_LL64__
+       std     r14,[sp,0]
+       std.aw  r18,[sp,-8]
+       std.aw  0xdeadbeef,[sp,-8]
+#else
+       st      r14,[sp,0]
+       st      r15,[sp,4]
+       st.aw   r19,[sp,-4]
+       st.aw   r18,[sp,-4]
+       sub     sp,sp,8
+#endif
+       add     sp,sp,24
+       j       [blink]
+
+; Store relative to some register with a known value.
+
+.align 4
+r_relative_store:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       mov     r13,sp
+       ; Check for both mov and mov_s in one testcase.
+       mov_s   r12,r13
+       st      r15,[r12,0]
+       st_s    r14,[sp,4]
+       add     sp,sp,12
+       j       [blink]
+
+; Store relative to some register with a known value using sub.
+; Like a previous test, but register is assigned via sub, instead of mov.
+
+.align 4
+r_relative_sub_store:
+       ; Following is a complicated way to construct frame like this:
+       ; sub_s sp,sp,12
+       ; st_s  r13,[sp,8]
+       ; st_s  r14,[sp,4]
+       ; st_s  r15,[sp,0]
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       sub     r13,sp,4
+       st      r14,[r13,8]
+       st_s    r15,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Like r_relative_store, but using st_s c,[b,u7] which has different opcode.
+
+.align 4
+r_relative_store_st_s:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       mov     r13,sp
+       st_s    r15,[r13,4]
+       st_s    r14,[sp,0]
+       add     sp,sp,12
+       j       [blink]
+
+; Store relative to some register with a unknown value.
+
+.align 4
+r_relative_store_unknown:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       st      r15,[gp,0] ; GP value is not relative to SP.
+       st_s    r14,[sp,4]
+       add     sp,sp,12
+       j       [blink]
+
+; Store relative to some register with a unknown value, using st_s r0,[gp,s11].
+
+.align 4
+st_s_r0gp:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,8]
+       st_s    r0,[gp,0] ; GP value is not relative to SP.
+       st_s    r14,[sp,4]
+       add     sp,sp,12
+       j       [blink]
+
+; Check prologue that uses `push_s RR` instructions. `push_s b` and `push_s
+; blink` use slightly different subopcodes.
+
+.align 4
+push_s_prologue:
+       push_s  r12
+       push_s  r0
+       push_s  r3
+       push_s  r13
+       push_s  r1
+       push_s  r14
+       push_s  r15
+       push_s  r2
+       push_s  blink ; Also tested in mini_prologue ().
+       add     sp,sp,(4 * 9)
+       j       [blink]
+
+; Check for SUB_S c,b,u3 presence - it doesn't affect prologue.
+
+.align 4
+sub_s_cbu3:
+       push_s  r13
+       sub_s   r0,r1,3
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check for SUB_S b,b,c presence - it doesn't affect prologue.
+
+.align 4
+sub_s_bbc:
+       push_s  r13
+       sub_s   r0,r0,r1
+       push_s  r0
+       push_s  r1
+       push_s  r14
+       add     sp,sp,16
+       j       [blink]
+
+; Check for SUB_S b,b,u5.
+
+.align 4
+sub_s_bbu5:
+       push_s  r13
+       sub_s   r2,r2,14
+       push_s  r2
+       push_s  r14
+       add     sp,sp,12
+       j       [blink]
+
+; Check for SUB 0,b,c, which is effectively a noop (but it can set status
+; flags).  It shouldn't stop prologue analysis.
+
+.align 4
+sub_0bc:
+       push_s  r13
+       sub     0,r1,r2
+       sub.f   0,r3,r4
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check for SUB a,limm,c.
+
+.align 4
+sub_alimmb:
+       push_s  r13
+       sub     r13,0xdeadbeef,r14
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check for sub_s.ne b,b,b.  Has a condition code, hence should halt prologue.
+
+.align 4
+sub_s_ne_bbb:
+       push_s  r13
+       sub_s.ne  r13,r13,r13
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check MOV that uses LIMM values.
+
+.align 4
+mov_limm:
+       push_s  r13
+       mov     r13,0xdeadbeef
+       push_s  r14
+       add     sp,sp,4
+       pop_s   r13
+       j       [blink]
+
+; Check MOV 0,c.
+
+.align 4
+mov0c_limm:
+       push_s  r13
+       mov     0,r13
+       push_s  r14
+       add     sp,sp,4
+       pop_s   r13
+       j       [blink]
+
+; Check that MOV_S h,s3 doesn't prevent prologue analysis.
+
+.align 4
+mov_s_hs3:
+       push_s  r13
+       mov_s   r5,1
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check that MOV_S b,u8 doesn't prevent prologue analysis.
+
+.align 4
+mov_s_bu8:
+       push_s  r13
+       mov_s   r12,250
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check that `mov_s.ne b,h` halts prologue analysis.
+
+.align 4
+mov_s_ne_bh:
+       push_s  r13
+       mov_s.ne r13,r5
+       push_s  r14
+       add     sp,sp,8
+       j       [blink]
+
+; Check that register R12 which original value is not stored will not pop-up in
+; the "Saved registers" list.
+
+.align 4
+unstored_reg:
+       sub_s   sp,sp,12
+       st_s    r13,[sp,0]
+       st_s    r14,[sp,4]
+       mov     r12,0x42
+       st_s    r12,[sp,8]
+       add     sp,sp,12
+       j       [blink]
+
+; Two stores at the same adddress.  GDB should report only the R14.
+
+.align 4
+double_store:
+       sub_s   sp,sp,4
+       st_s    r13,[sp,0]
+       st_s    r14,[sp,0]
+       add     sp,sp,4
+       j       [blink]
+
+; Test for a case where callee has an alloca or anything else that might
+; modify stack dynamically in the function body - after the prologue.
+; This assumes that FP is set properly, so that GDB can use it - this holds
+; true for frames generated by GCC.
+
+.align 4
+alloca_outer:
+       sub     sp,sp,8
+       st      blink,[sp,4]
+       st      fp,[sp,0]
+       mov     fp,sp
+       add     r0,r1,r2 ; Not a prologue anymore.
+       sub     sp,sp,8
+       bl      @alloca_inner
+       add     sp,sp,8
+       ld      fp,[sp,0]
+       ld      blink,[sp,4]
+       j.d     [blink]
+       add     sp,sp,8
+
+.align 4
+alloca_inner:
+       push    r13
+       push    r14
+       add     sp,sp,8
+       j       [blink]
+
+
+.align 4
+main:
+       push    blink
+       # Create small section for GP-relative accesses.
+       push    gp
+       sub     sp,sp,16
+       add     gp,sp,8
+       bl      @standard_prologue
+       bl      @mini_prologue
+       bl      @no_subsp_prologue
+       bl      @leaf_prologue
+       bl      @pushfp_prologue
+       bl      @fp_prologue_with_store
+       bl      @noncallee_saved_regs_r12_st
+       bl      @noncallee_saved_regs_r12_push
+       bl      @noncallee_saved_regs_r2_push
+       bl      @noncallee_saved_regs_gp_push
+       bl      @noncallee_saved_regs_lp_count
+       bl      @noncallee_saved_regs_blink_out_of_prologue
+       bl      @arg_regs_fp
+       bl      @arg_regs_fp_mov_s
+       bl      @arg_regs_sp
+       bl      @enter_s_nop
+       bl      @enter_s_blink
+       bl      @enter_s_fp
+       bl      @enter_s_r13
+       bl      @enter_s_r15
+       bl      @enter_s_all
+       bl      @nested_prologue_outer
+       bl      @max_length_prologue
+       bl      @branch_in_prologue
+       bl      @cond_branch_in_prologue
+       bl      @jump_in_prologue
+       bl      @cond_jump_in_prologue
+       bl      @predicated_insn
+       bl      @loop_in_prologue
+       bl      @store_constant
+       bl      @st_c_limm
+       bl      @st_ab_writeback
+       bl      @st_as_writeback
+       bl      @sth_as_writeback
+       bl      @std_as_writeback
+       bl      @st_halfword
+       bl      @sts_halfword
+       bl      @st_byte
+       bl      @sts_byte
+       bl      @sts_byte_sp
+       bl      @st_double
+       bl      @r_relative_store
+       bl      @r_relative_sub_store
+       bl      @r_relative_store_st_s
+       bl      @r_relative_store_unknown
+       bl      @st_s_r0gp
+       bl      @push_s_prologue
+       bl      @sub_s_cbu3
+       bl      @sub_s_bbc
+       bl      @sub_s_bbu5
+       bl      @sub_0bc
+       bl      @sub_alimmb
+       bl      @sub_s_ne_bbb
+       bl      @mov_limm
+       bl      @mov0c_limm
+       bl      @mov_s_hs3
+       bl      @mov_s_bu8
+       bl      @mov_s_ne_bh
+       bl      @unstored_reg
+       bl      @double_store
+       bl      @alloca_outer
+       add     sp,sp,16
+       pop     gp
+       pop     blink
+       j_s     [blink]
+
+.align 4
diff --git a/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp b/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp
new file mode 100644 (file)
index 0000000..12a44b5
--- /dev/null
@@ -0,0 +1,201 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if {![istarget "arc*-*-*"]} then {
+    verbose "Skipping ARC prologue test."
+    return
+}
+
+standard_testfile .S
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+    return -1
+}
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return 0
+}
+
+# Convert list of saved registers and their offsets to a GDB string.
+proc saved_regs_to_str { savedregs funcname } {
+    set str ""
+    # If blink is stored, that it is present twice in saved regs - as blink and
+    # as pc.
+    set has_blink 0
+    set blink_addr 0
+    foreach r $savedregs {
+       if { [llength $r] == 1 } {
+           append str ".*$r at.*"
+       } else {
+           set name [lindex $r 0]
+           set offset [lindex $r 1]
+           set addr [get_hexadecimal_valueof "\$sp+$offset" 0 \
+                "get value of $name@sp+$offset in $funcname"]
+           append str "\\s*$name at $addr,?"
+           if { $name == "blink" } {
+               set has_blink 1
+               set blink_addr $addr
+           }
+       }
+    }
+    if { $has_blink == 1 } {
+       append str "\\s*pc at $blink_addr"
+    }
+    return $str
+}
+
+# Arguments:
+# funcname -   name of function to test
+# savedregs -  list of register saved in the frame.  Each entry can be either
+#              a string, where it is a register name, or it is a list of two
+#              items - name of register and it's offset relatively to SP in
+#              the memory.  SP value is at the moment of prologue end.
+# fp_offset -  if not an empty string, then proc will test that FP register
+#              has a value that is (SP + offset).
+
+proc prologue_test {funcname {savedregs ""} {fp_offset ""} } {
+    global hex
+    gdb_breakpoint $funcname temporary
+    gdb_continue_to_breakpoint $funcname
+    gdb_test "backtrace 10" \
+       "#0\[ \t\]*$hex in $funcname .*\r\n#1\[ \t\]*$hex in main.*" \
+       "backtrace in $funcname"
+    if { $savedregs != "" } {
+       set str [saved_regs_to_str $savedregs $funcname]
+       gdb_test "info frame" \
+           ".*Saved registers:$str" \
+           "saved registers in $funcname"
+    }
+    if { $fp_offset != "" } {
+       set sp [get_integer_valueof \$sp -1 "get value of sp in $funcname"]
+       set fp_val [expr $sp + $fp_offset]
+       set fp_real_val \
+           [get_integer_valueof \$fp 0 "get value of fp in $funcname"]
+       if { $fp_real_val != $fp_val } {
+           fail "check FP value in $funcname"
+       } else {
+           pass "check FP value in $funcname"
+       }
+    }
+}
+
+
+prologue_test "standard_prologue" { {r13 0} {r14 4} {r18 8} {blink 12} }
+prologue_test "mini_prologue" { {r13 0} {r14 8} {r15 4} {blink 12} }
+prologue_test "no_subsp_prologue" { {r13 8} {r20 4} {r25 0} {blink 12} }
+prologue_test "leaf_prologue" { {r13 0} {r15 4} }
+prologue_test "pushfp_prologue" { {r13 8} {r14 4} {fp 0} } 0
+prologue_test "fp_prologue_with_store" { {r13 12} {r14 8} {r15 0} {fp 4} } 4
+prologue_test "noncallee_saved_regs_r12_st" { {r12 0} {r13 4} }
+# Register offset is specified relatively to SP at the prologue end, so
+# "push r12" hasn't been executed at this moment.
+prologue_test "noncallee_saved_regs_r12_push" { {r12 0} {r13 4} }
+prologue_test "noncallee_saved_regs_r2_push" { {r2 0} {r13 4} }
+prologue_test "noncallee_saved_regs_gp_push" { {r25 4} {gp 0} }
+prologue_test "noncallee_saved_regs_lp_count" { {r25 4} {lp_count 0} }
+prologue_test "noncallee_saved_regs_blink_out_of_prologue" { {r25 8} {gp 4} \
+    {blink 0}}
+# Argument registers are not reported as "saved" regs.
+prologue_test "arg_regs_fp" { {r0 12} {r1 8} {r7 4} {r8 0} {fp 16} } 16
+prologue_test "arg_regs_fp_mov_s" { {r0 4} {r8 0} {fp 8} } 8
+prologue_test "arg_regs_sp" { {r0 0} {r1 4} {r7 8} {r8 12} {r13 16} {r14 20} }
+prologue_test "enter_s_nop"
+prologue_test "enter_s_blink" { {blink 0} }
+prologue_test "enter_s_fp" { {fp 0} } 0
+# Layout of registers as stored by enter_s doesn't conform to ARC ABI.
+prologue_test "enter_s_r13" { {r13 4} {fp 8} {blink 0} } 0
+prologue_test "enter_s_r15" { {r13 0} {r14 4} {r15 8} } 0
+# This enter_s saves GP, however because it is not a "calle-saved register",
+# GDB will not report it as "saved register" (but maybe it should). GP is at
+# offset 56.
+prologue_test "enter_s_all" { {r13 4} {r14 8} {r15 12} {r16 16} {r17 20} \
+    {r18 24} {r19 28} {r20 32} {r21 36} {r22 40} {r23 44} {r24 48} {r25 52} \
+    {gp 56} {fp 60} {blink 0} } 0
+
+# Test more levels of backtrace.
+gdb_breakpoint nested_prologue_inner temporary
+gdb_continue_to_breakpoint nested_prologue_inner
+gdb_test "backtrace 10" \
+    "#0\[ \t\]*$hex in nested_prologue_inner .*\r\n#1\[ \t\]*$hex in nested_prologue_outer .*\r\n#2\[ \t\]*$hex in main.*" \
+    "backtrace in nested_prologue_inner"
+set regs [saved_regs_to_str {r13 r18} "nested_prologue_inner"]
+gdb_test "info frame" ".*Saved registers:$regs" \
+    "saved registers in nested_prologue_inner"
+set regs [saved_regs_to_str {r14 r15 blink} "nested_prologue_inner"]
+gdb_test "info frame 1" ".*Saved registers:$regs" \
+    "saved registers in nested_prologue_outer"
+
+# sub sp,sp for local variables is part of prologue, hence should be added to
+# all of those offsets.
+prologue_test "max_length_prologue" { {r0 72} {r1 76} {r2 80} {r3 84} {r4 88} \
+    {r5 92} {r6 96} {r7 100} {r13 20} {r14 24} {r15 28} {r16 32} \
+    {r17 36} {r18 40} {r19 44} {r20 48} {r21 52} {r22 56} {r23 60} {r24 64} \
+    {r25 68} {fp 16} {blink 104} }
+
+prologue_test "branch_in_prologue" { {r13 0} }
+prologue_test "cond_branch_in_prologue" { {r13 4} }
+prologue_test "jump_in_prologue" { {r13 0} }
+prologue_test "cond_jump_in_prologue" { {r13 4} }
+prologue_test "predicated_insn" { {r13 8} {r15 0} }
+prologue_test "loop_in_prologue" { {r25 4} {lp_count 0} }
+prologue_test "store_constant" { {r13 8} {r14 4} }
+prologue_test "st_c_limm" { {r15 0} }
+prologue_test "st_ab_writeback" { {r13 8} {r14 4} {r15 0} }
+prologue_test "st_as_writeback" { {r13 8} {r14 4} {r15 0} }
+prologue_test "sth_as_writeback" { {r13 8} {r15 0} }
+prologue_test "std_as_writeback" { {r13 12} {r14 4} {r15 8} {r16 0} }
+prologue_test "st_halfword" { {r13 8} {r15 0} }
+prologue_test "sts_halfword" { {r13 8} {r15 0} }
+prologue_test "st_byte" { {r13 8} {r15 0} }
+prologue_test "sts_byte" { {r13 8} {r15 0} }
+prologue_test "sts_byte_sp" { {r13 8} {r15 0} }
+prologue_test "st_double" { {r14 16} {r15 20} {r18 8} {r19 12}}
+prologue_test "r_relative_store" { {r13 8} {r14 4} {r15 0} }
+prologue_test "r_relative_sub_store" { {r13 8} {r14 4} {r15 0} }
+prologue_test "r_relative_store_st_s" { {r13 8} {r14 0} {r15 4} }
+prologue_test "r_relative_store_unknown" { {r13 8} }
+prologue_test "st_s_r0gp" { {r13 8} }
+prologue_test "push_s_prologue" { {r0 28} {r1 16} {r2 4} {r3 24} {r12 32} \
+    {r13 20} {r14 12} {r15 8} {blink 0}}
+prologue_test "sub_s_cbu3" { {r13 4} {r14 0} }
+prologue_test "sub_s_bbc" { {r1 4} {r13 12} {r14 0} }
+prologue_test "sub_s_bbu5" { {r13 8} {r14 0} }
+prologue_test "sub_0bc" { {r13 4} {r14 0} }
+prologue_test "sub_alimmb" { {r13 4} {r14 0} }
+prologue_test "sub_s_ne_bbb" { {r13 0} }
+prologue_test "mov_limm" { {r13 4} {r14 0} }
+prologue_test "mov0c_limm" { {r13 4} {r14 0} }
+prologue_test "mov_s_hs3" { {r13 4} {r14 0} }
+prologue_test "mov_s_bu8" { {r13 4} {r14 0} }
+prologue_test "mov_s_ne_bh" { {r13 0} }
+prologue_test "unstored_reg" { {r13 0} {r14 4} }
+prologue_test "double_store" { {r14 0} }
+
+# alloca() tests
+gdb_breakpoint alloca_inner temporary
+gdb_continue_to_breakpoint alloca_inner
+gdb_test "backtrace 3" \
+    "#0\[ \t\]*$hex in alloca_inner .*\r\n#1\[ \t\]*$hex in alloca_outer .*\r\n#2\[ \t\]*$hex in main.*" \
+    "backtrace in alloca_inner"
+set regs [saved_regs_to_str {r13 r14} alloca_inner]
+gdb_test "info frame 0" ".*Saved registers:$regs" \
+    "saved registers in alloca_inner"
+set regs [saved_regs_to_str {fp blink} alloca_inner]
+gdb_test "info frame 1" ".*Saved registers:$regs" \
+    "saved registers in alloca_outer"
+