This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 5/5 v2] arc: Add prologue analysis


Changes in v2:

  * Use "prepare_for" in test cases.
  * Ensure that all test cases have unique names in gdb.sum.
  * Fix invalid indentation in test cases.

---

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/arc-tdep.c                                  | 498 ++++++++++++-
 gdb/doc/gdb.texinfo                             |   3 +-
 gdb/testsuite/gdb.arch/arc-analyze-prologue.S   | 903 ++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/arc-analyze-prologue.exp | 200 ++++++
 4 files changed, 1582 insertions(+), 22 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/arc-analyze-prologue.S
 create mode 100644 gdb/testsuite/gdb.arch/arc-analyze-prologue.exp

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 639f7dd..88f125d 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -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.
@@ -1124,6 +1537,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 *
@@ -1135,12 +1570,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;
@@ -1152,18 +1586,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;
 }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 344778b..d2b5fae 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -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
diff --git a/gdb/testsuite/gdb.arch/arc-analyze-prologue.S b/gdb/testsuite/gdb.arch/arc-analyze-prologue.S
new file mode 100644
index 0000000..90e1035
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-analyze-prologue.S
@@ -0,0 +1,903 @@
+; This testcase is part of GDB, the GNU debugger.
+
+; Copyright 2017 Synopsys 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
index 0000000..9042420
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp
@@ -0,0 +1,200 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 Synopsys 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"
+
-- 
2.8.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]