public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 5/5] arc: Add prologue analysis
  2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
@ 2017-02-14 10:01 ` Anton Kolesov
  2017-02-14 15:51   ` Eli Zaretskii
  2017-02-17 13:25   ` Pedro Alves
  2017-02-14 10:01 ` [PATCH 3/5] arc: Add "maintenance print arc" command prefix Anton Kolesov
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-02-14 10:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

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_fprintf_disasm): Likewise.
	(arc_disassemble_info): 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                                  | 526 +++++++++++++-
 gdb/arc-tdep.h                                  |   2 +
 gdb/doc/gdb.texinfo                             |   3 +-
 gdb/testsuite/gdb.arch/arc-analyze-prologue.S   | 903 ++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/arc-analyze-prologue.exp | 206 ++++++
 5 files changed, 1622 insertions(+), 18 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 a18f16a..9076c98 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.  */
+
 /* Implement the "write_pc" gdbarch method.
 
    In ARC PC register is a normal register so in most cases setting PC value
@@ -650,6 +682,419 @@ 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 fprintf_disasm from disasm.c.  */
+
+static int ATTRIBUTE_PRINTF (2, 3)
+arc_fprintf_disasm (void *stream, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  vfprintf_filtered ((struct ui_file *) stream, format, args);
+  va_end (args);
+  /* Something non -ve.  */
+  return 0;
+}
+
+/* Return properly initialized disassemble_info.  */
+
+struct disassemble_info
+arc_disassemble_info (struct gdbarch *gdbarch)
+{
+  struct disassemble_info di;
+  init_disassemble_info (&di, &null_stream, arc_fprintf_disasm);
+  di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+  di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  di.endian = gdbarch_byte_order (gdbarch);
+  di.read_memory_func = [](bfd_vma memaddr, gdb_byte *myaddr,
+			   unsigned int len, struct disassemble_info *info)
+    {
+      return target_read_code (memaddr, myaddr, len);
+    };
+  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
@@ -679,15 +1124,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.
@@ -837,6 +1286,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 *
@@ -848,12 +1319,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;
@@ -865,18 +1335,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/arc-tdep.h b/gdb/arc-tdep.h
index db7647d..ff21f51 100644
--- a/gdb/arc-tdep.h
+++ b/gdb/arc-tdep.h
@@ -123,6 +123,8 @@ arc_mach_is_arcv2 (struct gdbarch *gdbarch)
   return gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arcv2;
 }
 
+struct disassemble_info arc_disassemble_info (struct gdbarch *gdbarch);
+
 /* Function to access ARC disassembler.  Underlying opcodes disassembler will
    print an instruction into stream specified in the INFO, so if it is
    undesired, then this stream should be set to some invisible stream, but it
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8cb656a..92759ae 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22077,8 +22077,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..f126604
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-analyze-prologue.exp
@@ -0,0 +1,206 @@
+# 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 { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "" ] != "" } {
+    untested arc-analyze-prologue.exp
+    return -1
+}
+
+# Initialization routine.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+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 } {
+    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]
+	    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]
+	gdb_test "info frame" \
+	    ".*Saved registers:[saved_regs_to_str $savedregs]" \
+	    "saved registers in $funcname"
+    }
+    if { $fp_offset != "" } {
+	set sp [get_integer_valueof \$sp -1]
+	set fp_val [expr $sp + $fp_offset]
+	set fp_real_val [get_integer_valueof \$fp 0]
+	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}]
+    gdb_test "info frame" ".*Saved registers:$regs" \
+	"saved registers in nested_prologue_inner"
+    set regs [saved_regs_to_str {r14 r15 blink}]
+    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}]
+    gdb_test "info frame 0" ".*Saved registers:$regs" \
+	"saved registers in nested_prologue_inner"
+    set regs [saved_regs_to_str {fp blink}]
+    gdb_test "info frame 1" ".*Saved registers:$regs" \
+	"saved registers in nested_prologue_outer"
+
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 3/5] arc: Add "maintenance print arc" command prefix
  2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
  2017-02-14 10:01 ` [PATCH 5/5] arc: Add prologue analysis Anton Kolesov
@ 2017-02-14 10:01 ` Anton Kolesov
  2017-02-17 13:02   ` Pedro Alves
  2017-02-14 10:01 ` [PATCH 2/5] arc: Set section to ".text" when disassembling Anton Kolesov
  2017-02-17 13:26 ` [PATCH 1/5] arc: Align internal regnums with architectural regnums Pedro Alves
  4 siblings, 1 reply; 23+ messages in thread
From: Anton Kolesov @ 2017-02-14 10:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

Add an "arc" sublist to "maintenance print" command list.  The list is empty
for now, its purpose is to contain commands that print internal state of some
ARC-specific structures.

gdb/ChangeLog:

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

	* arc-tdep (maintenance_print_arc_list): New variable.
	(maintenance_print_arc_command): New function.
---
 gdb/arc-tdep.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index e4b2e7a..5495f2e 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -59,6 +59,10 @@ struct arc_frame_cache
 
 int arc_debug;
 
+/* List of "maintenance print arc" commands.  */
+
+static struct cmd_list_element *maintenance_print_arc_list = NULL;
+
 /* XML target description features.  */
 
 static const char core_v2_feature_name[] = "org.gnu.gdb.arc.core.v2";
@@ -1318,6 +1322,14 @@ arc_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file, "arc_dump_tdep: jb_pc = %i\n", tdep->jb_pc);
 }
 
+/* Wrapper for "maintenance print arc" list of commands.  */
+
+static void
+maintenance_print_arc_command (char *args, int from_tty)
+{
+  cmd_show_list (maintenance_print_arc_list, from_tty, "");
+}
+
 /* Suppress warning from -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_arc_tdep;
 
@@ -1331,6 +1343,13 @@ _initialize_arc_tdep (void)
 
   /* Register ARC-specific commands with gdb.  */
 
+  /* Add root prefix command for "maintenance print arc" commands.  */
+  add_prefix_cmd ("arc", class_maintenance, maintenance_print_arc_command,
+		  _("ARC-specific maintenance commands for printing GDB "
+		    "internal state."),
+		  &maintenance_print_arc_list, "maintenance print arc ", 0,
+		  &maintenanceprintlist);
+
   /* Debug internals for ARC GDB.  */
   add_setshow_zinteger_cmd ("arc", class_maintenance,
 			    &arc_debug,
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 4/5] arc: Add disassembler helper
  2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
@ 2017-02-14 10:01 ` Anton Kolesov
  2017-02-14 15:50   ` Eli Zaretskii
                     ` (2 more replies)
  2017-02-14 10:01 ` [PATCH 5/5] arc: Add prologue analysis Anton Kolesov
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-02-14 10:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

Add disassembler helper for GDB, that uses opcodes structure arc_instruction
and adds convenience functions to handle instruction operands.  This interface
solves at least those problems with arc_instruction:

  * Some instructions, like "push_s", have implicit operands which are not
    directly present in arc_instruction.
  * Operands of particular meaning, like branch/jump targets, have various
    locations and meaning depending on type of branch/target.
  * Access to operand value is abstracted into a separate function, so callee
    code shouldn't bother if operand value is an immediate value or in a
    register.

Testcases included in this commit are fairly limited - they test exclusively
branch instructions, something that will be used in software single stepping.
Most of the other parts of this disassembler helper are tested during prologue
analysis testing.

gdb/ChangeLog:

yyyy-mm-dd  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.
	* arc-tdep.h (arc_delayed_print_insn): Add function declaration.
	* arch/arc-insn.c: New file.
	* arch/arc-insn.h: Likewise.

gdb/doc/ChangeLog:

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

	* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".

gdb/testsuite/ChangeLog:

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

      * gdb.arch/arc-decode-insn.S: New file.
      * gdb.arch/arc-decode-insn.exp: Likewise.
---
 gdb/arc-tdep.c                             |   28 +-
 gdb/arc-tdep.h                             |    6 +
 gdb/arch/arc-insn.c                        |  275 ++++++++
 gdb/arch/arc-insn.h                        |   64 ++
 gdb/configure.tgt                          |    4 +-
 gdb/doc/gdb.texinfo                        |    4 +
 gdb/testsuite/gdb.arch/arc-decode-insn.S   | 1002 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/arc-decode-insn.exp |  136 ++++
 8 files changed, 1516 insertions(+), 3 deletions(-)
 create mode 100644 gdb/arch/arc-insn.c
 create mode 100644 gdb/arch/arc-insn.h
 create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.S
 create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.exp

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 5495f2e..a18f16a 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -33,6 +33,7 @@
 /* ARC header files.  */
 #include "opcode/arc.h"
 #include "arc-tdep.h"
+#include "arch/arc-insn.h"
 
 /* Standard headers.  */
 #include <algorithm>
@@ -701,7 +702,7 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
    that will not print, or `stream` should be different from standard
    gdb_stdlog.  */
 
-static int
+int
 arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
 {
   int (*print_insn) (bfd_vma, struct disassemble_info *);
@@ -1330,6 +1331,26 @@ maintenance_print_arc_command (char *args, int from_tty)
   cmd_show_list (maintenance_print_arc_list, from_tty, "");
 }
 
+/* This command accepts single argument - address of instruction to
+   disassemble.  */
+
+static void
+dump_arc_instruction_command (char *args, int from_tty)
+{
+  struct value *val;
+  if (args != NULL && strlen (args) > 0)
+    val = evaluate_expression (parse_expression (args).get ());
+  else
+    val = access_value_history (0);
+  record_latest_value (val);
+
+  CORE_ADDR address = value_as_address (val);
+  struct arc_instruction insn;
+  struct disassemble_info di = arc_disassemble_info (target_gdbarch ());
+  arc_insn_decode (address, &di, arc_delayed_print_insn, &insn);
+  arc_insn_dump (insn);
+}
+
 /* Suppress warning from -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_arc_tdep;
 
@@ -1350,6 +1371,11 @@ _initialize_arc_tdep (void)
 		  &maintenance_print_arc_list, "maintenance print arc ", 0,
 		  &maintenanceprintlist);
 
+  add_cmd ("arc-instruction", class_maintenance,
+	   dump_arc_instruction_command,
+	   _("Dump arc_instruction structure for specified address."),
+	   &maintenance_print_arc_list);
+
   /* Debug internals for ARC GDB.  */
   add_setshow_zinteger_cmd ("arc", class_maintenance,
 			    &arc_debug,
diff --git a/gdb/arc-tdep.h b/gdb/arc-tdep.h
index 326f486..db7647d 100644
--- a/gdb/arc-tdep.h
+++ b/gdb/arc-tdep.h
@@ -123,4 +123,10 @@ arc_mach_is_arcv2 (struct gdbarch *gdbarch)
   return gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arcv2;
 }
 
+/* Function to access ARC disassembler.  Underlying opcodes disassembler will
+   print an instruction into stream specified in the INFO, so if it is
+   undesired, then this stream should be set to some invisible stream, but it
+   can't be set to an actual NULL value - that would cause a crash.  */
+int arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info);
+
 #endif /* ARC_TDEP_H */
diff --git a/gdb/arch/arc-insn.c b/gdb/arch/arc-insn.c
new file mode 100644
index 0000000..2dc99a7
--- /dev/null
+++ b/gdb/arch/arc-insn.c
@@ -0,0 +1,275 @@
+/* ARC disassembler helper.
+
+   Copyright 2017 Free Software Foundation, Inc.
+   Contributed by Synopsys Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+/* GDB header files.  */
+#include "defs.h"
+#include "disasm.h"
+
+/* ARC header files.  */
+#include "opcode/arc.h"
+#include "arc-tdep.h"
+#include "arch/arc-insn.h"
+
+void
+arc_insn_dump (const struct arc_instruction &insn)
+{
+  struct gdbarch *gdbarch = target_gdbarch ();
+
+  arc_print ("Dumping arc_instruction at %s\n",
+	     paddress (gdbarch, insn.address));
+  arc_print ("\tlength=%u\n", insn.length);
+
+  if (!insn.valid)
+    {
+      arc_print ("\tThis is not a valid ARC instruction.\n");
+      return;
+    }
+
+  arc_print ("\tlength_with_limm=%u\n", insn.length + (insn.limm_p ? 4 : 0));
+  arc_print ("\tcc=0x%x\n", insn.condition_code);
+  arc_print ("\tinsn_class=%u\n", insn.insn_class);
+  arc_print ("\tis_control_flow=%i\n", insn.is_control_flow);
+  arc_print ("\thas_delay_slot=%i\n", insn.has_delay_slot);
+
+  CORE_ADDR next_pc = arc_insn_get_linear_next_pc (insn);
+  arc_print ("\tlinear_next_pc=%s\n", paddress (gdbarch, next_pc));
+
+  if (insn.is_control_flow)
+    {
+      CORE_ADDR t = arc_insn_get_branch_target (insn);
+      arc_print ("\tbranch_target=%s\n", paddress (gdbarch, t));
+    }
+
+  arc_print ("\tlimm_p=%i\n", insn.limm_p);
+  if (insn.limm_p)
+    arc_print ("\tlimm_value=0x%08x\n", insn.limm_value);
+
+  if (insn.insn_class == STORE || insn.insn_class == LOAD
+      || insn.insn_class == PUSH || insn.insn_class == POP)
+    {
+      arc_print ("\twriteback_mode=%u\n", insn.writeback_mode);
+      arc_print ("\tdata_size_mode=%u\n", insn.data_size_mode);
+      arc_print ("\tmemory_base_register=%s\n",
+		 gdbarch_register_name (gdbarch,
+					arc_insn_get_memory_base_reg (insn)));
+      /* get_memory_offset returns an unsigned CORE_ADDR, but treat it as a
+	 LONGEST for a nicer representation.  */
+      arc_print ("\taddr_offset=%s\n",
+		 plongest (arc_insn_get_memory_offset (insn)));
+    }
+
+  arc_print ("\toperands_count=%u\n", insn.operands_count);
+  for (unsigned int i = 0; i < insn.operands_count; ++i)
+    {
+      int is_reg = (insn.operands[i].kind == ARC_OPERAND_KIND_REG);
+
+      arc_print ("\toperand[%u] = {\n", i);
+      arc_print ("\t\tis_reg=%i\n", is_reg);
+      if (is_reg)
+	arc_print ("\t\tregister=%s\n",
+		   gdbarch_register_name (gdbarch, insn.operands[i].value));
+      /* Don't know if this value is signed or not, so print both
+	 representations.  This tends to look quite ugly, especially for big
+	 numbers.  */
+      arc_print ("\t\tunsigned value=%s\n",
+		 pulongest (arc_insn_get_operand_value (insn, i)));
+      arc_print ("\t\tsigned value=%s\n",
+		 plongest (arc_insn_get_operand_value_signed (insn, i)));
+      arc_print ("\t}\n");
+    }
+}
+
+CORE_ADDR
+arc_insn_get_linear_next_pc (const struct arc_instruction &insn)
+{
+  /* In ARC long immediate is always 4 bytes.  */
+  return (insn.address + insn.length + (insn.limm_p ? 4 : 0));
+}
+
+CORE_ADDR
+arc_insn_get_branch_target (const struct arc_instruction &insn)
+{
+  gdb_assert (insn.is_control_flow);
+
+  /* BI [c]: PC = nextPC + (c << 2).  */
+  if (insn.insn_class == BI)
+    {
+      ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+      return arc_insn_get_linear_next_pc (insn) + (reg_value << 2);
+    }
+  /* BIH [c]: PC = nextPC + (c << 1).  */
+  else if (insn.insn_class == BIH)
+    {
+      ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+      return arc_insn_get_linear_next_pc (insn) + (reg_value << 1);
+    }
+  /* JLI and EI.  */
+  /* JLI and EI depend on optional AUX registers.  Not supported right now.  */
+  else if (insn.insn_class == JLI)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "JLI_S instruction is not supported by the GDB.");
+      return 0;
+    }
+  else if (insn.insn_class == EI)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "EI_S instruction is not supported by the GDB.");
+      return 0;
+    }
+  /* LEAVE_S: PC = BLINK.  */
+  else if (insn.insn_class == LEAVE)
+    {
+      struct regcache *regcache = get_current_regcache ();
+      ULONGEST value;
+      regcache_cooked_read_unsigned (regcache, ARC_BLINK_REGNUM, &value);
+      return value;
+    }
+  /* BBIT0/1, BRcc: PC = currentPC + operand.  */
+  else if (insn.insn_class == BBIT0 || insn.insn_class == BBIT1
+	   || insn.insn_class == BRCC)
+    {
+      /* Most instructions has branch target as their sole argument.  However
+	 conditional brcc/bbit has it as a third operand.  */
+      CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 2);
+
+      /* Offset is relative to the 4-byte aligned address of the current
+	 instruction, hence last two bits should be truncated.  */
+      return pcrel_addr + align_down (insn.address, 4);
+    }
+  /* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand.  */
+  else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
+    {
+      CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 0);
+
+      /* Offset is relative to the 4-byte aligned address of the current
+	 instruction, hence last two bits should be truncated.  */
+      return pcrel_addr + align_down (insn.address, 4);
+    }
+  /* J, Jcc, JL, JLcc: PC = operand.  */
+  else if (insn.insn_class == JUMP)
+    {
+      /* All jumps are single-operand.  */
+      return arc_insn_get_operand_value (insn, 0);
+    }
+
+  /* This is some new and unknown instruction.  */
+  gdb_assert_not_reached ("Unknown branch instruction.");
+}
+
+ULONGEST
+arc_insn_get_operand_value (const struct arc_instruction &insn,
+			    unsigned int operand_num)
+{
+  switch (insn.operands[operand_num].kind)
+    {
+    case ARC_OPERAND_KIND_LIMM:
+      gdb_assert (insn.limm_p);
+      return insn.limm_value;
+    case ARC_OPERAND_KIND_SHIMM:
+      return insn.operands[operand_num].value;
+    default:
+      /* Value in instruction is a register number.  */
+      struct regcache *regcache = get_current_regcache ();
+      ULONGEST value;
+      regcache_cooked_read_unsigned (regcache,
+				     insn.operands[operand_num].value,
+				     &value);
+      return value;
+    }
+}
+
+LONGEST
+arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+				   unsigned int operand_num)
+{
+  switch (insn.operands[operand_num].kind)
+    {
+    case ARC_OPERAND_KIND_LIMM:
+      gdb_assert (insn.limm_p);
+      /* Convert unsigned raw value to signed one.  This assumes 2's
+	 complement arithmetic, but so is the LONG_MIN value from generic
+	 defs.h and that assumption is true for ARC.  */
+      gdb_static_assert (sizeof (insn.limm_value) == sizeof (int));
+      return (((LONGEST) insn.limm_value) ^ INT_MIN) - INT_MIN;
+    case ARC_OPERAND_KIND_SHIMM:
+      /* Sign conversion has been done by binutils.  */
+      return insn.operands[operand_num].value;
+    default:
+      /* Value in instruction is a register number.  */
+      struct regcache *regcache = get_current_regcache ();
+      LONGEST value;
+      regcache_cooked_read_signed (regcache,
+				   insn.operands[operand_num].value,
+				   &value);
+      return value;
+    }
+}
+
+int
+arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
+{
+  /* POP_S and PUSH_S have SP as an implicit argument in a disassembler.  */
+  if (insn.insn_class == PUSH || insn.insn_class == POP)
+    return ARC_SP_REGNUM;
+
+  gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+  /* Other instructions all have at least two operands: operand 0 is data,
+     operand 1 is address.  Operand 2 is offset from address.  However, see
+     comment to arc_instruction.operands - in some cases, third operand may be
+     missing, namely if it is 0.  */
+  gdb_assert (insn.operands_count >= 2);
+  return insn.operands[1].value;
+}
+
+CORE_ADDR
+arc_insn_get_memory_offset (const struct arc_instruction &insn)
+{
+  /* POP_S and PUSH_S have offset as an implicit argument in a
+     disassembler.  */
+  if (insn.insn_class == POP)
+    return 4;
+  else if (insn.insn_class == PUSH)
+    return -4;
+
+  gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+  /* Other instructions all have at least two operands: operand 0 is data,
+     operand 1 is address.  Operand 2 is offset from address.  However, see
+     comment to arc_instruction.operands - in some cases, third operand may be
+     missing, namely if it is 0.  */
+  if (insn.operands_count < 3)
+    return 0;
+
+  CORE_ADDR value = arc_insn_get_operand_value (insn, 2);
+  /* Handle scaling.  */
+  if (insn.writeback_mode == ARC_WRITEBACK_AS)
+    {
+      /* Byte data size is not valid for AS.  Halfword means shift by 1 bit.
+	 Word and double word means shift by 2 bits.  */
+      gdb_assert (insn.data_size_mode != ARC_SCALING_B);
+      if (insn.data_size_mode == ARC_SCALING_H)
+	value <<= 1;
+      else
+	value <<= 2;
+    }
+  return value;
+}
diff --git a/gdb/arch/arc-insn.h b/gdb/arch/arc-insn.h
new file mode 100644
index 0000000..e25d226
--- /dev/null
+++ b/gdb/arch/arc-insn.h
@@ -0,0 +1,64 @@
+/* ARC disassembler helper.
+
+   Copyright 2017 Free Software Foundation, Inc.
+   Contributed by Synopsys Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef ARC_INSN_H
+#define ARC_INSN_H
+
+#include "opcodes/arc-dis.h"
+
+/* Dump INSN into gdb_stdlog.  */
+
+void arc_insn_dump (const struct arc_instruction &insn);
+
+/* Get address of next instruction after INSN, assuming linear execution (no
+   taken branches).  If instruction has a delay slot, then returned value will
+   point at the instruction in delay slot.  That is - "address of instruction +
+   instruction length with LIMM".  */
+
+CORE_ADDR arc_insn_get_linear_next_pc (const struct arc_instruction &insn);
+
+/* Get branch/jump target address for the INSN.  Note that this function
+   returns branch target and doesn't evaluate if this branch is taken or not.
+   For the indirect jumps value depends in register state, hence can change.
+   It is an error to call this function for a non-branch instruction.  */
+
+CORE_ADDR arc_insn_get_branch_target (const struct arc_instruction &insn);
+
+/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
+   For relative branch instructions returned value is an offset, not an actual
+   branch target.  */
+
+ULONGEST arc_insn_get_operand_value (const struct arc_instruction &insn,
+				     unsigned int operand_num);
+
+/* Like arc_insn_get_operand_value, but returns a signed value.  */
+
+LONGEST arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+					   unsigned int operand_num);
+
+/* Get register with base address of memory operation.  */
+
+int arc_insn_get_memory_base_reg (const struct arc_instruction &insn);
+
+/* Get offset of a memory operation INSN.  */
+
+CORE_ADDR arc_insn_get_memory_offset (const struct arc_instruction &insn);
+
+#endif /* ARC_INSN_H */
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index cb909e7..532ac54 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -86,12 +86,12 @@ am33_2.0*-*-linux*)
 
 arc*-*-elf32)
 	# Target: baremetal ARC elf32 (newlib) target
-	gdb_target_obs="arc-newlib-tdep.o arc-tdep.o"
+	gdb_target_obs="arc-insn.o arc-newlib-tdep.o arc-tdep.o"
 	;;
 
 arc*-*-*)
 	# Target: Unidentified ARC target
-	gdb_target_obs="arc-tdep.o"
+	gdb_target_obs="arc-insn.o arc-tdep.o"
 	;;
 
 arm*-wince-pe | arm*-*-mingw32ce*)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f619470..8cb656a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22084,6 +22084,10 @@ messages.
 @kindex show debug arc
 Show the level of ARC specific debugging in operation.
 
+@item maint print arc arc-instruction @var{address}
+@kindex maint print arc arc-instruction
+Print internal disassembler information about instruction at a given address.
+
 @end table
 
 @node ARM
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.S b/gdb/testsuite/gdb.arch/arc-decode-insn.S
new file mode 100644
index 0000000..030f158
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.S
@@ -0,0 +1,1002 @@
+; 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 .text
+.global main
+
+#define TEST_J
+#define TEST_JCC
+#define TEST_JL
+#define TEST_JLCC
+#define TEST_B
+#define TEST_BBIT
+#define TEST_BCC
+#define TEST_BI
+#define TEST_BL
+#define TEST_BRCC
+#define TEST_JLI
+#define TEST_LEAVE_S
+#define TEST_LPCC
+
+; JLI-specific stuff
+#ifdef TEST_JLI
+jli_table:
+    .word 0xdeadbeea
+    .word 0xdeadbeea
+jli_target:
+    .word 0xdeadbeea
+    .word 0xdeadbeea
+
+.set jli_offset, 3
+#endif
+main:
+
+; Each test case requires several symbols to be set, that identify expected
+; parameters of this instruction.  Required symbols:
+;   ${test}_start: symbol points to start of the test
+;   ${test}_end: symbol points to the instruction after the jump/branch
+;      instruction.
+;   ${test}_target: branch target address.
+;   ${test}_has_delay_slot: whether instruction has delay slot.
+;   ${test}_cc: condition code numeric value.
+
+    .set r12_value, 0xdead0000
+    .set blink_value, 0xdead0004
+    .set limm_value, 0xdead0008
+    ; Just an integer
+    .set r4_value, 0xdead000c
+    ; Just an integer
+    .set r5_value, 0xdead0010
+    ; offset index for BI [c]
+    .set r7_value, 4
+    .set u6_value, 0x20
+    .set s12_target, 0x100
+
+    mov r12, @r12_value
+    mov r4, @r4_value
+    mov r5, @r5_value
+    mov r7, @r7_value
+    mov blink, @blink_value
+#ifdef TEST_JLI
+    ; jli_base aux regnum = 0x290
+    sr jli_table, [0x290]
+#endif
+
+start_branch_tests:
+
+#ifdef TEST_J
+
+#define TEST_NAME j_c
+    ; j [c]
+    .set j_c_target, @r4_value
+    .set j_c_has_delay_slot,   0
+    .set j_c_cc, 0
+    j_c_start:
+	j [r4]
+    j_c_end:
+
+    ; j [blink]
+    .set j_blink_target, @blink_value
+    .set j_blink_has_delay_slot,   0
+    .set j_blink_cc, 0
+    mov blink, @j_blink_target
+    j_blink_start:
+	j [blink]
+    j_blink_end:
+
+    ; j limm
+    .set j_limm_target, @limm_value
+    .set j_limm_has_delay_slot,   0
+    .set j_limm_cc, 0
+    j_limm_start:
+	j @j_limm_target
+    j_limm_end:
+
+    ; j u6
+    .set j_u6_target, @u6_value
+    .set j_u6_has_delay_slot,   0
+    .set j_u6_cc, 0
+    j_u6_start:
+	j @j_u6_target
+    j_u6_end:
+
+    ; j s12
+    .set j_s12_target, @s12_target
+    .set j_s12_has_delay_slot,   0
+    .set j_s12_cc, 0
+    j_s12_start:
+	j @j_s12_target
+    j_s12_end:
+
+    ; j.d [c]
+    .set j_d_c_target, @r4_value
+    .set j_d_c_has_delay_slot,   1
+    .set j_d_c_cc, 0
+    j_d_c_start:
+	j.d [r4]
+    j_d_c_end:
+	nop_s
+
+    ; j.d [blink]
+    .set j_d_blink_target, @blink_value
+    .set j_d_blink_has_delay_slot,   1
+    .set j_d_blink_cc, 0
+    j_d_blink_start:
+	j.d [blink]
+    j_d_blink_end:
+	nop_s
+
+    ; j.d u6
+    .set j_d_u6_target, @u6_value
+    .set j_d_u6_has_delay_slot,   1
+    .set j_d_u6_cc, 0
+    j_d_u6_start:
+	j.d @j_d_u6_target
+    j_d_u6_end:
+	nop_s
+
+    ; j.d s12
+    .set j_d_s12_target, @s12_target
+    .set j_d_s12_has_delay_slot,   1
+    .set j_d_s12_cc, 0
+    j_d_s12_start:
+	j.d @j_d_s12_target
+    j_d_s12_end:
+	nop_s
+
+    ; j_s [b]
+    .set j_s_b_target, @r12_value
+    .set j_s_b_has_delay_slot,   0
+    .set j_s_b_cc, 0
+    j_s_b_start:
+	j_s [r12]
+    j_s_b_end:
+
+    ; j_s.d [b]
+    .set j_s_d_b_target, @r12_value
+    .set j_s_d_b_has_delay_slot,   1
+    .set j_s_d_b_cc, 0
+    j_s_d_b_start:
+	j_s.d [r12]
+    j_s_d_b_end:
+	nop_s
+
+    ; j_s [blink]
+    .set j_s_blink_target, @blink_value
+    .set j_s_blink_has_delay_slot,   0
+    .set j_s_blink_cc, 0
+    j_s_blink_start:
+	j_s [blink]
+    j_s_blink_end:
+
+    ; j_s.d [blink]
+    .set j_s_d_blink_target, @blink_value
+    .set j_s_d_blink_has_delay_slot,   1
+    .set j_c_cc, 0
+    j_s_d_blink_start:
+	j_s.d [blink]
+    j_s_d_blink_end:
+	nop_s
+#endif /* TEST_J */
+
+#ifdef TEST_JCC
+    ; jcc [c]
+    .set jcc_c_target, @r4_value
+    .set jcc_c_has_delay_slot,   0
+    .set jcc_c_cc, 1
+    jcc_c_start:
+	jeq [r4]
+    jcc_c_end:
+
+    ; jcc [blink]
+    .set jcc_blink_target, @blink_value
+    .set jcc_blink_has_delay_slot,   0
+    .set jcc_blink_cc, 2
+    jcc_blink_start:
+	jnz [blink]
+    jcc_blink_end:
+
+    ; jcc limm
+    .set jcc_limm_target, @limm_value
+    .set jcc_limm_has_delay_slot,   0
+    .set jcc_limm_cc, 9
+    jcc_limm_start:
+	jgt @jcc_limm_target
+    jcc_limm_end:
+
+    ; jcc u6
+    .set jcc_u6_target, @u6_value
+    .set jcc_u6_has_delay_slot,   0
+    .set jcc_u6_cc, 0xA
+    jcc_u6_start:
+	jge @jcc_u6_target
+    jcc_u6_end:
+
+    ; jcc.d [c]
+    .set jcc_d_c_target, @r4_value
+    .set jcc_d_c_has_delay_slot,   1
+    .set jcc_d_c_cc, 0xB
+    jcc_d_c_start:
+	jlt.d [r4]
+    jcc_d_c_end:
+	nop_s
+
+    ; jcc.d [blink]
+    .set jcc_d_blink_target, @blink_value
+    .set jcc_d_blink_has_delay_slot,   1
+    .set jcc_d_blink_cc, 0xC
+    jcc_d_blink_start:
+	jle.d [blink]
+    jcc_d_blink_end:
+	nop_s
+
+    ; jcc.d u6
+    .set jcc_d_u6_target, @u6_value
+    .set jcc_d_u6_has_delay_slot,   1
+    .set jcc_d_u6_cc, 0xE
+    jcc_d_u6_start:
+	jls.d @jcc_d_u6_target
+    jcc_d_u6_end:
+	nop_s
+
+    ; jeq_s [blink]
+    .set jcc_eq_s_blink_target, @blink_value
+    .set jcc_eq_s_blink_has_delay_slot,   0
+    .set jcc_eq_s_blink_cc, 1
+    jcc_eq_s_blink_start:
+	jeq_s [blink]
+    jcc_eq_s_blink_end:
+
+    ; jne_s [blink]
+    .set jcc_ne_s_blink_target, @blink_value
+    .set jcc_ne_s_blink_has_delay_slot,   0
+    .set jcc_ne_s_blink_cc, 2
+    jcc_ne_s_blink_start:
+	jne_s [blink]
+    jcc_ne_s_blink_end:
+#endif /* TEST_JCC */
+
+#ifdef TEST_JL
+    ; jl [c]
+    .set jl_c_target, @r4_value
+    .set jl_c_has_delay_slot,   0
+    .set jl_c_cc, 0
+    jl_c_start:
+	jl [r4]
+    jl_c_end:
+
+    ; jl limm
+    .set jl_limm_target, @limm_value
+    .set jl_limm_has_delay_slot,   0
+    .set jl_limm_cc, 0
+    jl_limm_start:
+	jl @jl_limm_target
+    jl_limm_end:
+
+    ; jl u6
+    .set jl_u6_target, @u6_value
+    .set jl_u6_has_delay_slot,   0
+    .set jl_u6_cc, 0
+    jl_u6_start:
+	jl @jl_u6_target
+    jl_u6_end:
+
+    ; jl s12
+    .set jl_s12_target, @s12_target
+    .set jl_s12_has_delay_slot,   0
+    .set jl_s12_cc, 0
+    jl_s12_start:
+	jl @jl_s12_target
+    jl_s12_end:
+
+    ; jl.d [c]
+    .set jl_d_c_target, @r4_value
+    .set jl_d_c_has_delay_slot,   1
+    .set jl_d_c_cc, 0
+    jl_d_c_start:
+	jl.d [r4]
+    jl_d_c_end:
+	nop_s
+
+    ; jl.d u6
+    .set jl_d_u6_target, @u6_value
+    .set jl_d_u6_has_delay_slot,   1
+    .set jl_d_u6_cc, 0
+    jl_d_u6_start:
+	jl.d @jl_d_u6_target
+    jl_d_u6_end:
+	nop_s
+
+    ; jl.d s12
+    .set jl_d_s12_target, @s12_target
+    .set jl_d_s12_has_delay_slot,   1
+    .set jl_d_s12_cc, 0
+    jl_d_s12_start:
+	jl.d @jl_d_s12_target
+    jl_d_s12_end:
+	nop_s
+
+    ; jl_s [b]
+    .set jl_s_b_target, @r12_value
+    .set jl_s_b_has_delay_slot,   0
+    .set jl_s_b_cc, 0
+    jl_s_b_start:
+	jl_s [r12]
+    jl_s_b_end:
+
+    ; jl_s.d [b]
+    .set jl_s_d_b_target, @r12_value
+    .set jl_s_d_b_has_delay_slot,   1
+    .set jl_s_d_b_cc, 0
+    jl_s_d_b_start:
+	jl_s.d [r12]
+    jl_s_d_b_end:
+	nop_s
+#endif /* TEST_JL */
+
+#ifdef TEST_JLCC
+    ; jlcc [c]
+    .set jlcc_c_target, @r4_value
+    .set jlcc_c_has_delay_slot,   0
+    .set jlcc_c_cc, 1
+    jlcc_c_start:
+	jleq [r4]
+    jlcc_c_end:
+
+    ; jlcc limm
+    .set jlcc_limm_target, @limm_value
+    .set jlcc_limm_has_delay_slot,   0
+    .set jlcc_limm_cc, 0x9
+    jlcc_limm_start:
+	jlgt @jlcc_limm_target
+    jlcc_limm_end:
+
+    ; jlcc u6
+    .set jlcc_u6_target, @u6_value
+    .set jlcc_u6_has_delay_slot,   0
+    .set jlcc_u6_cc, 0xA
+    jlcc_u6_start:
+	jlge @jlcc_u6_target
+    jlcc_u6_end:
+
+    ; jlcc.d [c]
+    .set jlcc_d_c_target, @r4_value
+    .set jlcc_d_c_has_delay_slot,   1
+    .set jlcc_d_c_cc, 0xB
+    jlcc_d_c_start:
+	jllt.d [r4]
+    jlcc_d_c_end:
+	nop_s
+
+    ; jlcc.d u6
+    .set jlcc_d_u6_target, @u6_value
+    .set jlcc_d_u6_has_delay_slot,   1
+    .set jlcc_d_u6_cc, 0xE
+    jlcc_d_u6_start:
+	jlls.d @jlcc_d_u6_target
+    jlcc_d_u6_end:
+	nop_s
+#endif /* TEST_JLCC */
+
+#ifdef TEST_B
+.Lb_target:
+    ; Artifical nop, so that first b will not branch to itself.
+    nop_s
+    ; b s25
+    .set b_s25_target, @.Lb_target
+    .set b_s25_has_delay_slot,   0
+    .set b_s25_cc, 0
+    b_s25_start:
+	b @b_s25_target
+    b_s25_end:
+
+    ; b.d s25
+    .set b_d_s25_target, @.Lb_target
+    .set b_d_s25_has_delay_slot,   1
+    .set b_d_s25_cc, 0
+    b_d_s25_start:
+	b.d @b_d_s25_target
+    b_d_s25_end:
+	nop_s
+
+    ; b_s s10
+    .set b_s_s10_target, @.Lb_target
+    .set b_s_s10_has_delay_slot,   0
+    .set b_s_s10_cc, 0
+    b_s_s10_start:
+	b_s @b_s_s10_target
+    b_s_s10_end:
+#endif /* TEST_B */
+
+#ifdef TEST_BBIT
+
+; Due to specifics of bbit implementation in assembler, only local symbols can
+; be used as a branch targets for bbit and brcc.
+; bbits and brcc don't have condition code set to anything.
+.Lbbit_target:
+    nop_s
+
+    ; bbit0.nt b,c,s9
+    .set bbit0_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_c_s9_has_delay_slot,   0
+    .set bbit0_nt_b_c_s9_cc, 0
+    bbit0_nt_b_c_s9_start:
+	bbit0.nt r4,r5,@bbit0_nt_b_c_s9_target
+    bbit0_nt_b_c_s9_end:
+
+    ; bbit0.d.nt b,c,s9
+    .set bbit0_d_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit0_d_nt_b_c_s9_has_delay_slot,   1
+    .set bbit0_d_nt_b_c_s9_cc, 0
+    bbit0_d_nt_b_c_s9_start:
+	bbit0.d.nt r4,r5,@.Lbbit_target
+    bbit0_d_nt_b_c_s9_end:
+	nop_s
+
+    ; bbit0.t b,c,s9
+    .set bbit0_t_b_c_s9_target, @.Lbbit_target
+    .set bbit0_t_b_c_s9_has_delay_slot,   0
+    .set bbit0_t_b_c_s9_cc, 0
+    bbit0_t_b_c_s9_start:
+	bbit0.t r4,r5,@.Lbbit_target
+    bbit0_t_b_c_s9_end:
+
+    ; bbit0.d.t b,c,s9
+    .set bbit0_d_t_b_c_s9_target, @.Lbbit_target
+    .set bbit0_d_t_b_c_s9_has_delay_slot,   1
+    .set bbit0_d_t_b_c_s9_cc, 0
+    bbit0_d_t_b_c_s9_start:
+	bbit0.d.t r4,r5,@.Lbbit_target
+    bbit0_d_t_b_c_s9_end:
+	nop_s
+
+    ; bbit0.nt b,u6,s9
+    .set bbit0_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_u6_s9_has_delay_slot,   0
+    .set bbit0_nt_b_u6_s9_cc, 0
+    bbit0_nt_b_u6_s9_start:
+	bbit0.nt r4,u6_value,@.Lbbit_target
+    bbit0_nt_b_u6_s9_end:
+
+    ; bbit0.d.nt b,u6,s9
+    .set bbit0_d_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_d_nt_b_u6_s9_has_delay_slot,   1
+    .set bbit0_d_nt_b_u6_s9_cc, 0
+    bbit0_d_nt_b_u6_s9_start:
+	bbit0.d.nt r4,u6_value,@.Lbbit_target
+    bbit0_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; bbit0.nt b,u6,s9
+    .set bbit0_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_t_b_u6_s9_has_delay_slot,   0
+    .set bbit0_t_b_u6_s9_cc, 0
+    bbit0_t_b_u6_s9_start:
+	bbit0.t r4,u6_value,@.Lbbit_target
+    bbit0_t_b_u6_s9_end:
+
+    ; bbit0.d.nt b,u6,s9
+    .set bbit0_d_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_d_t_b_u6_s9_has_delay_slot,   1
+    .set bbit0_d_t_b_u6_s9_cc, 0
+    bbit0_d_t_b_u6_s9_start:
+	bbit0.d.t r4,u6_value,@.Lbbit_target
+    bbit0_d_t_b_u6_s9_end:
+	nop_s
+
+    ; bbit0.nt b,limm,s9
+    .set bbit0_nt_b_limm_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_limm_s9_has_delay_slot,   0
+    .set bbit0_nt_b_limm_s9_cc, 0
+    bbit0_nt_b_limm_s9_start:
+	bbit0.nt r4,limm_value,@.Lbbit_target
+    bbit0_nt_b_limm_s9_end:
+
+    ; bbit0.t b,limm,s9
+    .set bbit0_t_b_limm_s9_target, @.Lbbit_target
+    .set bbit0_t_b_limm_s9_has_delay_slot,   0
+    .set bbit0_t_b_limm_s9_cc, 0
+    bbit0_t_b_limm_s9_start:
+	bbit0.t r4,limm_value,@.Lbbit_target
+    bbit0_t_b_limm_s9_end:
+
+    ; bbit0.nt limm,c,s9
+    .set bbit0_nt_limm_c_s9_target, @.Lbbit_target
+    .set bbit0_nt_limm_c_s9_has_delay_slot,   0
+    .set bbit0_nt_limm_c_s9_cc, 0
+    bbit0_nt_limm_c_s9_start:
+	bbit0.nt limm_value,r4,@.Lbbit_target
+    bbit0_nt_limm_c_s9_end:
+
+    ; bbit0.t limm,c,s9
+    .set bbit0_t_limm_c_s9_target, @.Lbbit_target
+    .set bbit0_t_limm_c_s9_has_delay_slot,   0
+    .set bbit0_t_limm_c_s9_cc, 0
+    bbit0_t_limm_c_s9_start:
+	bbit0.t limm_value,r4,@.Lbbit_target
+    bbit0_t_limm_c_s9_end:
+
+    ; bbit0.nt limm,u6,s9
+    .set bbit0_nt_limm_u6_s9_target, @.Lbbit_target
+    .set bbit0_nt_limm_u6_s9_has_delay_slot,   0
+    .set bbit0_nt_limm_u6_s9_cc, 0
+    bbit0_nt_limm_u6_s9_start:
+	bbit0.nt limm_value,u6_value,@.Lbbit_target
+    bbit0_nt_limm_u6_s9_end:
+
+    ; bbit0.t limm,u6,s9
+    .set bbit0_t_limm_u6_s9_target, @.Lbbit_target
+    .set bbit0_t_limm_u6_s9_has_delay_slot,   0
+    .set bbit0_t_limm_u6_s9_cc, 0
+    bbit0_t_limm_u6_s9_start:
+	bbit0.t limm_value,u6_value,@.Lbbit_target
+    bbit0_t_limm_u6_s9_end:
+
+    ; bbit1.nt b,c,s9
+    .set bbit1_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_c_s9_has_delay_slot,   0
+    .set bbit1_nt_b_c_s9_cc, 0
+    bbit1_nt_b_c_s9_start:
+	bbit1.nt r4,r5,@.Lbbit_target
+    bbit1_nt_b_c_s9_end:
+
+    ; bbit1.d.nt b,c,s9
+    .set bbit1_d_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit1_d_nt_b_c_s9_has_delay_slot,   1
+    .set bbit1_d_nt_b_c_s9_cc, 0
+    bbit1_d_nt_b_c_s9_start:
+	bbit1.d.nt r4,r5,@.Lbbit_target
+    bbit1_d_nt_b_c_s9_end:
+	nop_s
+
+    ; bbit1.t b,c,s9
+    .set bbit1_t_b_c_s9_target, @.Lbbit_target
+    .set bbit1_t_b_c_s9_has_delay_slot,   0
+    .set bbit1_t_b_c_s9_cc, 0
+    bbit1_t_b_c_s9_start:
+	bbit1.t r4,r5,@.Lbbit_target
+    bbit1_t_b_c_s9_end:
+
+    ; bbit1.d.t b,c,s9
+    .set bbit1_d_t_b_c_s9_target, @.Lbbit_target
+    .set bbit1_d_t_b_c_s9_has_delay_slot,   1
+    .set bbit1_d_t_b_c_s9_cc, 0
+    bbit1_d_t_b_c_s9_start:
+	bbit1.d.t r4,r5,@.Lbbit_target
+    bbit1_d_t_b_c_s9_end:
+	nop_s
+
+    ; bbit1.nt b,u6,s9
+    .set bbit1_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_u6_s9_has_delay_slot,   0
+    .set bbit1_nt_b_u6_s9_cc, 0
+    bbit1_nt_b_u6_s9_start:
+	bbit1.nt r4,u6_value,@.Lbbit_target
+    bbit1_nt_b_u6_s9_end:
+
+    ; bbit1.d.nt b,u6,s9
+    .set bbit1_d_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_d_nt_b_u6_s9_has_delay_slot,   1
+    .set bbit1_d_nt_b_u6_s9_cc, 0
+    bbit1_d_nt_b_u6_s9_start:
+	bbit1.d.nt r4,u6_value,@.Lbbit_target
+    bbit1_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; bbit1.nt b,u6,s9
+    .set bbit1_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_t_b_u6_s9_has_delay_slot,   0
+    .set bbit1_t_b_u6_s9_cc, 0
+    bbit1_t_b_u6_s9_start:
+	bbit1.t r4,u6_value,@.Lbbit_target
+    bbit1_t_b_u6_s9_end:
+
+    ; bbit1.d.nt b,u6,s9
+    .set bbit1_d_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_d_t_b_u6_s9_has_delay_slot,   1
+    .set bbit1_d_t_b_u6_s9_cc, 0
+    bbit1_d_t_b_u6_s9_start:
+	bbit1.d.t r4,u6_value,@.Lbbit_target
+    bbit1_d_t_b_u6_s9_end:
+	nop_s
+
+    ; bbit1.nt b,limm,s9
+    .set bbit1_nt_b_limm_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_limm_s9_has_delay_slot,   0
+    .set bbit1_nt_b_limm_s9_cc, 0
+    bbit1_nt_b_limm_s9_start:
+	bbit1.nt r4,limm_value,@.Lbbit_target
+    bbit1_nt_b_limm_s9_end:
+
+    ; bbit1.t b,limm,s9
+    .set bbit1_t_b_limm_s9_target, @.Lbbit_target
+    .set bbit1_t_b_limm_s9_has_delay_slot,   0
+    .set bbit1_t_b_limm_s9_cc, 0
+    bbit1_t_b_limm_s9_start:
+	bbit1.t r4,limm_value,@.Lbbit_target
+    bbit1_t_b_limm_s9_end:
+
+    ; bbit1.nt limm,c,s9
+    .set bbit1_nt_limm_c_s9_target, @.Lbbit_target
+    .set bbit1_nt_limm_c_s9_has_delay_slot,   0
+    .set bbit1_nt_limm_c_s9_cc, 0
+    bbit1_nt_limm_c_s9_start:
+	bbit1.nt limm_value,r4,@.Lbbit_target
+    bbit1_nt_limm_c_s9_end:
+
+    ; bbit1.t limm,c,s9
+    .set bbit1_t_limm_c_s9_target, @.Lbbit_target
+    .set bbit1_t_limm_c_s9_has_delay_slot,   0
+    .set bbit1_t_limm_c_s9_cc, 0
+    bbit1_t_limm_c_s9_start:
+	bbit1.t limm_value,r4,@.Lbbit_target
+    bbit1_t_limm_c_s9_end:
+
+    ; bbit1.nt limm,u6,s9
+    .set bbit1_nt_limm_u6_s9_target, @.Lbbit_target
+    .set bbit1_nt_limm_u6_s9_has_delay_slot,   0
+    .set bbit1_nt_limm_u6_s9_cc, 0
+    bbit1_nt_limm_u6_s9_start:
+	bbit1.nt limm_value,u6_value,@.Lbbit_target
+    bbit1_nt_limm_u6_s9_end:
+
+    ; bbit1.t limm,u6,s9
+    .set bbit1_t_limm_u6_s9_target, @.Lbbit_target
+    .set bbit1_t_limm_u6_s9_has_delay_slot,   0
+    .set bbit1_t_limm_u6_s9_cc, 0
+    bbit1_t_limm_u6_s9_start:
+	bbit1.t limm_value,u6_value,@.Lbbit_target
+    bbit1_t_limm_u6_s9_end:
+#endif /* TEST_BBIT */
+
+#ifdef TEST_BCC
+.Lbcc_target:
+    ; bcc s21
+    .set bcc_s21_target, @.Lbcc_target
+    .set bcc_s21_has_delay_slot,   0
+    .set bcc_s21_cc, 1
+    bcc_s21_start:
+	; beq @bcc_s21_target
+	beq @.Lbcc_target
+    bcc_s21_end:
+
+    ; bcc.d s21
+    .set bcc_d_s21_target, @.Lbcc_target
+    .set bcc_d_s21_has_delay_slot,   1
+    .set bcc_d_s21_cc, 1
+    bcc_d_s21_start:
+	beq.d @bcc_d_s21_target
+    bcc_d_s21_end:
+	nop_s
+
+.Lbcc_s_target:
+    ; beq_s s10
+    .set beq_s_s10_target, @.Lbcc_s_target
+    .set beq_s_s10_has_delay_slot,   0
+    .set beq_s_s10_cc, 1
+    beq_s_s10_start:
+	# beq_s.d @beq_s_s10_target
+	beq_s @.Lbcc_s_target
+    beq_s_s10_end:
+
+    ; bne_s s10
+    .set bne_s_s10_target, @.Lbcc_s_target
+    .set bne_s_s10_has_delay_slot,   0
+    .set bne_s_s10_cc, 2
+    bne_s_s10_start:
+	bne_s @bne_s_s10_target
+    bne_s_s10_end:
+
+    ; bgt_s s7
+    .set bgt_s_s7_target, @.Lbcc_s_target
+    .set bgt_s_s7_has_delay_slot,   0
+    .set bgt_s_s7_cc, 0x9
+    bgt_s_s7_start:
+	bgt_s @bgt_s_s7_target
+    bgt_s_s7_end:
+
+    ; bge_s s7
+    .set bge_s_s7_target, @.Lbcc_s_target
+    .set bge_s_s7_has_delay_slot,   0
+    .set bge_s_s7_cc, 0xA
+    bge_s_s7_start:
+	bge_s @bge_s_s7_target
+    bge_s_s7_end:
+
+    ; blt_s s7
+    .set blt_s_s7_target, @.Lbcc_s_target
+    .set blt_s_s7_has_delay_slot,   0
+    .set blt_s_s7_cc, 0xB
+    blt_s_s7_start:
+	blt_s @blt_s_s7_target
+    blt_s_s7_end:
+
+    ; ble_s s7
+    .set ble_s_s7_target, @.Lbcc_s_target
+    .set ble_s_s7_has_delay_slot,   0
+    .set ble_s_s7_cc, 0xC
+    ble_s_s7_start:
+	ble_s @ble_s_s7_target
+    ble_s_s7_end:
+
+    ; bhi_s s7
+    .set bhi_s_s7_target, @.Lbcc_s_target
+    .set bhi_s_s7_has_delay_slot,   0
+    .set bhi_s_s7_cc, 0xD
+    bhi_s_s7_start:
+	bhi_s @bhi_s_s7_target
+    bhi_s_s7_end:
+
+    ; bhs_s s7
+    .set bhs_s_s7_target, @.Lbcc_s_target
+    .set bhs_s_s7_has_delay_slot,   0
+    .set bhs_s_s7_cc, 0x6
+    bhs_s_s7_start:
+	bhs_s @bhs_s_s7_target
+    bhs_s_s7_end:
+
+    ; blo_s s7
+    .set blo_s_s7_target, @.Lbcc_s_target
+    .set blo_s_s7_has_delay_slot,   0
+    .set blo_s_s7_cc, 0x5
+    blo_s_s7_start:
+	blo_s @blo_s_s7_target
+    blo_s_s7_end:
+
+    ; bls_s s7
+    .set bls_s_s7_target, @.Lbcc_s_target
+    .set bls_s_s7_has_delay_slot,   0
+    .set bls_s_s7_cc, 0xE
+    bls_s_s7_start:
+	bls_s @bls_s_s7_target
+    bls_s_s7_end:
+#endif /* TEST_BCC */
+
+#ifdef TEST_BI
+    ; bi [c]
+    .set bi_c_target, @bi_c_end + (@r7_value << 2)
+    .set bi_c_has_delay_slot,   0
+    .set bi_c_cc, 0
+    bi_c_start:
+	bi [r7]
+    bi_c_end:
+
+    ; bih [c]
+    .set bih_c_target, @bih_c_end + (@r7_value << 1)
+    .set bih_c_has_delay_slot,   0
+    .set bih_c_cc, 0
+    bih_c_start:
+	bih [r7]
+    bih_c_end:
+#endif /* TEST_BI */
+
+#ifdef TEST_BL
+.Lbl_target:
+    ; bl s25
+    .set bl_s25_target, @.Lbl_target
+    .set bl_s25_has_delay_slot,   0
+    .set bl_s25_cc, 0
+    bl_s25_start:
+	bl @bl_s25_target
+    bl_s25_end:
+
+    ; bl.d s25
+    .set bl_d_s25_target, @.Lbl_target
+    .set bl_d_s25_has_delay_slot,   1
+    .set bl_d_s25_cc, 0
+    bl_d_s25_start:
+	bl.d @bl_d_s25_target
+    bl_d_s25_end:
+	nop_s
+
+    ; bl_s s13
+    .set bl_s_s13_target, @.Lbl_target
+    .set bl_s_s13_has_delay_slot,   0
+    .set bl_s_s13_cc, 0
+    bl_s_s13_start:
+	bl_s @bl_s_s13_target
+    bl_s_s13_end:
+
+    ; blcc s21
+    .set blcc_s21_target, @.Lbl_target
+    .set blcc_s21_has_delay_slot,   0
+    .set blcc_s21_cc, 1
+    blcc_s21_start:
+	bleq @blcc_s21_target
+    blcc_s21_end:
+
+    ; blcc.d s21
+    .set blcc_d_s21_target, @.Lbl_target
+    .set blcc_d_s21_has_delay_slot,   1
+    .set blcc_d_s21_cc, 2
+    blcc_d_s21_start:
+	blnz.d @blcc_d_s21_target
+    blcc_d_s21_end:
+	nop_s
+#endif /* TEST_BL */
+
+#ifdef TEST_BRCC
+.Lbrcc_target:
+    ; breq.nt b,c,s9
+    .set breq_nt_b_c_s9_target, @.Lbrcc_target
+    .set breq_nt_b_c_s9_has_delay_slot,   0
+    .set breq_nt_b_c_s9_cc, 1
+    breq_nt_b_c_s9_start:
+	breq.nt r4,r5,@.Lbrcc_target
+    breq_nt_b_c_s9_end:
+
+    ; breq.d.nt b,c,s9
+    .set breq_d_nt_b_c_s9_target, @.Lbrcc_target
+    .set breq_d_nt_b_c_s9_has_delay_slot,   1
+    .set breq_d_nt_b_c_s9_cc, 1
+    breq_d_nt_b_c_s9_start:
+	breq.d.nt r4,r5,@.Lbrcc_target
+    breq_d_nt_b_c_s9_end:
+	nop_s
+
+    ; breq.t b,c,s9
+    .set breq_t_b_c_s9_target, @.Lbrcc_target
+    .set breq_t_b_c_s9_has_delay_slot,   0
+    .set breq_t_b_c_s9_cc, 1
+    breq_t_b_c_s9_start:
+	breq.t r4,r5,@.Lbrcc_target
+    breq_t_b_c_s9_end:
+
+    ; breq.d.t b,c,s9
+    .set breq_d_t_b_c_s9_target, @.Lbrcc_target
+    .set breq_d_t_b_c_s9_has_delay_slot,   1
+    .set breq_d_t_b_c_s9_cc, 1
+    breq_d_t_b_c_s9_start:
+	breq.d.t r4,r5,@.Lbrcc_target
+    breq_d_t_b_c_s9_end:
+	nop_s
+
+    ; breq.nt b,u6,s9
+    .set breq_nt_b_u6_s9_target, @.Lbrcc_target
+    .set breq_nt_b_u6_s9_has_delay_slot,   0
+    .set breq_nt_b_u6_s9_cc, 1
+    breq_nt_b_u6_s9_start:
+	breq.nt r4,u6_value,@.Lbrcc_target
+    breq_nt_b_u6_s9_end:
+
+    ; breq.d.nt b,u6,s9
+    .set breq_d_nt_b_u6_s9_target, @.Lbrcc_target
+    .set breq_d_nt_b_u6_s9_has_delay_slot,   1
+    .set breq_d_nt_b_u6_s9_cc, 1
+    breq_d_nt_b_u6_s9_start:
+	breq.d.nt r4,u6_value,@.Lbrcc_target
+    breq_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; breq.nt b,u6,s9
+    .set breq_t_b_u6_s9_target, @.Lbrcc_target
+    .set breq_t_b_u6_s9_has_delay_slot,   0
+    .set breq_t_b_u6_s9_cc, 1
+    breq_t_b_u6_s9_start:
+	breq.t r4,u6_value,@.Lbrcc_target
+    breq_t_b_u6_s9_end:
+
+    ; breq.d.nt b,u6,s9
+    .set breq_d_t_b_u6_s9_target, @.Lbrcc_target
+    .set breq_d_t_b_u6_s9_has_delay_slot,   1
+    .set breq_d_t_b_u6_s9_cc, 1
+    breq_d_t_b_u6_s9_start:
+	breq.d.t r4,u6_value,@.Lbrcc_target
+    breq_d_t_b_u6_s9_end:
+	nop_s
+
+    ; breq.nt b,limm,s9
+    .set breq_nt_b_limm_s9_target, @.Lbrcc_target
+    .set breq_nt_b_limm_s9_has_delay_slot,   0
+    .set breq_nt_b_limm_s9_cc, 1
+    breq_nt_b_limm_s9_start:
+	breq.nt r4,limm_value,@.Lbrcc_target
+    breq_nt_b_limm_s9_end:
+
+    ; breq.t b,limm,s9
+    .set breq_t_b_limm_s9_target, @.Lbrcc_target
+    .set breq_t_b_limm_s9_has_delay_slot,   0
+    .set breq_t_b_limm_s9_cc, 1
+    breq_t_b_limm_s9_start:
+	breq.t r4,limm_value,@.Lbrcc_target
+    breq_t_b_limm_s9_end:
+
+    ; breq.nt limm,c,s9
+    .set breq_nt_limm_c_s9_target, @.Lbrcc_target
+    .set breq_nt_limm_c_s9_has_delay_slot,   0
+    .set breq_nt_limm_c_s9_cc, 1
+    breq_nt_limm_c_s9_start:
+	breq.nt limm_value,r4,@.Lbrcc_target
+    breq_nt_limm_c_s9_end:
+
+    ; breq.t limm,c,s9
+    .set breq_t_limm_c_s9_target, @.Lbrcc_target
+    .set breq_t_limm_c_s9_has_delay_slot,   0
+    .set breq_t_limm_c_s9_cc, 1
+    breq_t_limm_c_s9_start:
+	breq.t limm_value,r4,@.Lbrcc_target
+    breq_t_limm_c_s9_end:
+
+    ; breq.nt limm,u6,s9
+    .set breq_nt_limm_u6_s9_target, @.Lbrcc_target
+    .set breq_nt_limm_u6_s9_has_delay_slot,   0
+    .set breq_nt_limm_u6_s9_cc, 1
+    breq_nt_limm_u6_s9_start:
+	breq.nt limm_value,u6_value,@.Lbrcc_target
+    breq_nt_limm_u6_s9_end:
+
+    ; breq.t limm,u6,s9
+    .set breq_t_limm_u6_s9_target, @.Lbrcc_target
+    .set breq_t_limm_u6_s9_has_delay_slot,   0
+    .set breq_t_limm_u6_s9_cc, 1
+    breq_t_limm_u6_s9_start:
+	breq.t limm_value,u6_value,@.Lbrcc_target
+    breq_t_limm_u6_s9_end:
+
+    ; brne_s b,0,s8
+    .set brne_s_b_0_s8_target, @.Lbrcc_target
+    .set brne_s_b_0_s8_has_delay_slot,   0
+    .set brne_s_b_0_s8_cc, 1
+    brne_s_b_0_s8_start:
+	brne r12,0,@.Lbrcc_target
+    brne_s_b_0_s8_end:
+
+    ; breq_s b,0,s8
+    .set breq_s_b_0_s8_target, @.Lbrcc_target
+    .set breq_s_b_0_s8_has_delay_slot,   0
+    .set breq_s_b_0_s8_cc, 1
+    breq_s_b_0_s8_start:
+	breq r12,0,@.Lbrcc_target
+    breq_s_b_0_s8_end:
+#endif /* TEST_BRCC */
+
+#ifdef TEST_JLI
+    ; jli_s u10
+    .set jli_s_u10_target, @jli_target
+    .set jli_s_u10_has_delay_slot,   0
+    .set jli_s_u10_cc, 0
+    jli_s_u10_start:
+	jli_s jli_offset
+    jli_s_u10_end:
+#endif
+
+#ifdef TEST_LEAVE_S
+    ; leave_s
+    .set leave_s_target, @blink_value
+    .set leave_s_has_delay_slot,   0
+    .set leave_s_cc, 0
+    leave_s_start:
+	; leave_s [r13-gp,fp,blink,pcl]
+	leave_s (14 + 16 + 32 + 64)
+    leave_s_end:
+#endif
+
+#ifdef TEST_LPCC
+    ; lpcc
+    .set lpcc_u7_target, @.Llpcc_end
+    .set lpcc_u7_has_delay_slot,   0
+    .set lpcc_u7_cc, 1
+    lpcc_u7_start:
+	lpeq @lpcc_u7_target
+    lpcc_u7_end:
+	nop
+	nop
+.Llpcc_end:
+#endif
+
+.Lend:
+
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.exp b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
new file mode 100644
index 0000000..329b4ef
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
@@ -0,0 +1,136 @@
+# 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/>.
+
+
+# This tests provides certain degree of testing for arch/arc-insn.c functions,
+# however it is not a comprehensive testsuite that would go through all
+# possible ARC instructions - instead this particular test is focused on branch
+# instructions and whether branch targets are evaluated properly.  Most of the
+# non-branch aspects of instruction decoder are used during prologue analysis,
+# so are indirictly tested there.
+
+# To maintain separation of test data and test logic, all of the information
+# about instructions, like if it has delay slot, condition code, branch target
+# address, is all specified in the test assembly file as a symbols, while this
+# test case reads those symbols to learn which values are right, then compares
+# values coming from arc-insn.c with those found in symbols.  More information
+# about requirements to actual test cases can be found in corresponding
+# assembly file of this test case (arc-decode-insn.S).
+
+if {![istarget "arc*-*-*"]} then {
+    verbose "Skipping ARC decoder test."
+    return
+}
+
+standard_testfile .S
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "" ] != "" } {
+    untested arc-decode-insn.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] {
+    fail "Can't run to main"
+    return 0
+}
+
+# Helper function that reads properties of instruction from the ELF file via
+# its symbols and then confirms that decoder output aligns to the expected
+# values.
+proc test_branch_insn { test_name } {
+
+    # Make messages for failed cases more clear, by using hex in them.
+    set pc [get_hexadecimal_valueof &${test_name}_start -1]
+
+    # Calculate instruction length, based on ${test_name}_end symbol.
+    set end_pc [get_hexadecimal_valueof &${test_name}_end -1]
+    set length [expr $end_pc - $pc]
+
+    set target_address [get_hexadecimal_valueof &${test_name}_target -1]
+
+    # Figure out if there is a delay slot, using symbol
+    # ${test_name}_has_delay_slot.  Note that it should be read via &,
+    # otherwise it would try to print value at the address specified in
+    # ${test_name}_has_delay_slot, while a symbol value itself is required.
+    if { 0 == [get_integer_valueof &${test_name}_has_delay_slot 0] } {
+	set has_delay_slot 0
+    } else {
+	set has_delay_slot 1
+    }
+
+    set cc [get_hexadecimal_valueof &${test_name}_cc 0]
+
+    gdb_test_sequence "mt print arc arc-instruction $pc" "" "
+	length_with_limm=$length
+	cc=$cc
+	is_control_flow=1
+	has_delay_slot=$has_delay_slot
+	branch_target=$target_address"
+}
+
+set branch_test_list { }
+
+# Add items in the same groups as they can be enabled/disabled in assembly
+# file.
+lappend branch_test_list \
+    j_c j_blink j_limm j_u6 j_s12 j_d_c j_d_blink j_d_u6
+lappend branch_test_list \
+    jcc_c jcc_blink jcc_limm jcc_u6 jcc_d_c jcc_d_blink jcc_d_u6 jcc_eq_s_blink \
+    jcc_ne_s_blink
+lappend branch_test_list \
+    jl_c jl_limm jl_u6 jl_s12 jl_d_c jl_d_u6 jl_d_s12 jl_s_b jl_s_d_b
+lappend branch_test_list \
+    jlcc_c jlcc_limm jlcc_u6 jlcc_d_c jlcc_d_u6
+lappend branch_test_list \
+    b_s25 b_d_s25 b_s_s10
+lappend branch_test_list \
+    bbit0_nt_b_c_s9 bbit0_d_nt_b_c_s9 bbit0_t_b_c_s9 bbit0_d_t_b_c_s9 \
+    bbit0_nt_b_u6_s9 bbit0_d_nt_b_u6_s9 bbit0_t_b_u6_s9 bbit0_d_t_b_u6_s9 \
+    bbit0_nt_b_limm_s9 bbit0_t_b_limm_s9 bbit0_nt_limm_c_s9 bbit0_t_limm_c_s9 \
+    bbit0_nt_limm_u6_s9 bbit0_t_limm_u6_s9 \
+    bbit1_nt_b_c_s9 bbit1_d_nt_b_c_s9 bbit1_t_b_c_s9 bbit1_d_t_b_c_s9 \
+    bbit1_nt_b_u6_s9 bbit1_d_nt_b_u6_s9 bbit1_t_b_u6_s9 bbit1_d_t_b_u6_s9 \
+    bbit1_nt_b_limm_s9 bbit1_t_b_limm_s9 bbit1_nt_limm_c_s9 bbit1_t_limm_c_s9 \
+    bbit1_nt_limm_u6_s9 bbit1_t_limm_u6_s9
+lappend branch_test_list \
+    bcc_s21 bcc_d_s21 \
+    beq_s_s10 bne_s_s10 bgt_s_s7 bge_s_s7 blt_s_s7 ble_s_s7 bhi_s_s7 bhs_s_s7 \
+    blo_s_s7 bls_s_s7
+lappend branch_test_list \
+    bi_c bih_c
+lappend branch_test_list \
+    bl_s25 bl_d_s25 bl_s_s13 \
+    blcc_s21 blcc_d_s21
+lappend branch_test_list \
+     breq_nt_b_c_s9 breq_d_nt_b_c_s9 breq_t_b_c_s9 breq_d_t_b_c_s9 \
+     breq_nt_b_u6_s9 breq_d_nt_b_u6_s9 breq_t_b_u6_s9 breq_d_t_b_u6_s9 \
+     breq_nt_b_limm_s9 breq_t_b_limm_s9 breq_nt_limm_c_s9 breq_t_limm_c_s9 \
+     breq_nt_limm_u6_s9 breq_t_limm_u6_s9
+# lappend branch_test_list jli_s_u10
+lappend branch_test_list leave_s
+lappend branch_test_list lpcc_u7
+
+runto start_branch_tests
+foreach test $branch_test_list {
+    test_branch_insn $test
+}
+
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 2/5] arc: Set section to ".text" when disassembling
  2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
                   ` (2 preceding siblings ...)
  2017-02-14 10:01 ` [PATCH 3/5] arc: Add "maintenance print arc" command prefix Anton Kolesov
@ 2017-02-14 10:01 ` Anton Kolesov
  2017-02-15 22:27   ` Yao Qi
  2017-02-17 13:26 ` [PATCH 1/5] arc: Align internal regnums with architectural regnums Pedro Alves
  4 siblings, 1 reply; 23+ messages in thread
From: Anton Kolesov @ 2017-02-14 10:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

ARC disassembler requires that disassemble_info->section is valid - it will be
used to access program headers of ELF files that is needed to distinguish
between ARC EM and HS (ARC600, ARC700 and ARC v2 can be distinguished by BFD's
`mach` attribute).  Without this information disassembler will default to ARC
EM for ARC v2 targets, and it will not recognize instructions specific to ARC
HS, for example, double-word store and load.

gdb/ChangeLog:

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

	* arc-tdep.c (arc_delayed_print_insn): Set info->section.
---
 gdb/arc-tdep.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index f78e3a9..e4b2e7a 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -705,6 +705,16 @@ arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
      will handle NULL value gracefully.  */
   print_insn = arc_get_disassembler (exec_bfd);
   gdb_assert (print_insn != NULL);
+
+  /* ARC disassembler requires that info->section is valid - it will be used to
+     access program headers of ELF files that is needed to distinguish between
+     ARC EM and HS (ARC600, ARC700 and ARC v2 can be distinguished by BFD's
+     `mach` attribute).  Without this information disassembler will default to
+     ARC EM for ARC v2 targets, and it will not recognize instructions specific
+     to ARC HS, for example, double-word store and load.  */
+  if (exec_bfd != NULL)
+    info->section = bfd_get_section_by_name (exec_bfd, ".text");
+
   return print_insn (addr, info);
 }
 
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/5] arc: Align internal regnums with architectural regnums
@ 2017-02-14 10:01 Anton Kolesov
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-02-14 10:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

Add ARC_LIMM_REGNUM to arc_regnum enumeration and assign a number 62 to it.
This ensures that for core registers internal register numbers in this enum are
the same as architectural numbers.  This allows to use internal register
numbers in the contexts where architectural number is implied, for example when
disassembling instruction during prologue analysis.

gdb/ChangeLog:

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

	* arc-tdep.c (core_v2_register_names, core_arcompact_register_names)
	Add "limm" and "reserved".
	(arc_cannot_fetch_register, arc_cannot_store_register): Add
	ARC_RESERVED_REGNUM and ARC_LIMM_REGNUM.
	* arc-tdep.h (arc_regnum): Likewise.
---
 gdb/arc-tdep.c | 28 +++++++++++++++++++++-------
 gdb/arc-tdep.h | 16 ++++++++++++++++
 2 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 0800d7b..f78e3a9 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -86,7 +86,7 @@ static const char *const core_v2_register_names[] = {
   "r48", "r49", "r50", "r51",
   "r52", "r53", "r54", "r55",
   "r56", "r57", "accl", "acch",
-  "lp_count", "pcl",
+  "lp_count", "reserved", "limm", "pcl",
 };
 
 static const char *const aux_minimal_register_names[] = {
@@ -109,7 +109,7 @@ static const char *const core_arcompact_register_names[] = {
   "r48", "r49", "r50", "r51",
   "r52", "r53", "r54", "r55",
   "r56", "r57", "r58", "r59",
-  "lp_count", "pcl",
+  "lp_count", "reserved", "limm", "pcl",
 };
 
 /* Implement the "write_pc" gdbarch method.
@@ -430,8 +430,19 @@ arc_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr,
 static int
 arc_cannot_fetch_register (struct gdbarch *gdbarch, int regnum)
 {
-  /* Assume that register is readable if it is unknown.  */
-  return FALSE;
+  /* Assume that register is readable if it is unknown.  LIMM and RESERVED are
+     not real registers, but specific register numbers.  They are available as
+     regnums to align architectural register numbers with GDB internal regnums,
+     but they shouldn't appear in target descriptions generated by
+     GDB-servers.  */
+  switch (regnum)
+    {
+    case ARC_RESERVED_REGNUM:
+    case ARC_LIMM_REGNUM:
+      return true;
+    default:
+      return false;
+    }
 }
 
 /* Implement the "cannot_store_register" gdbarch method.  */
@@ -439,13 +450,16 @@ arc_cannot_fetch_register (struct gdbarch *gdbarch, int regnum)
 static int
 arc_cannot_store_register (struct gdbarch *gdbarch, int regnum)
 {
-  /* Assume that register is writable if it is unknown.  */
+  /* Assume that register is writable if it is unknown.  See comment in
+     arc_cannot_fetch_register about LIMM and RESERVED.  */
   switch (regnum)
     {
+    case ARC_RESERVED_REGNUM:
+    case ARC_LIMM_REGNUM:
     case ARC_PCL_REGNUM:
-      return TRUE;
+      return true;
     default:
-      return FALSE;
+      return false;
     }
 }
 
diff --git a/gdb/arc-tdep.h b/gdb/arc-tdep.h
index 422db46..326f486 100644
--- a/gdb/arc-tdep.h
+++ b/gdb/arc-tdep.h
@@ -24,6 +24,12 @@
 /* Need disassemble_info.  */
 #include "dis-asm.h"
 
+/* To simplify GDB code this enum assumes that internal regnums should be same
+   as architectural register numbers, i.e. PCL regnum is 63.  This allows to
+   use internal GDB regnums as architectural numbers when dealing with
+   instruction encodings, for example when analyzing what are the registers
+   saved in function prologue.  */
+
 enum arc_regnum
   {
     /* Core registers.  */
@@ -49,6 +55,16 @@ enum arc_regnum
     ARC_BLINK_REGNUM,
     /* Zero-delay loop counter.  */
     ARC_LP_COUNT_REGNUM = 60,
+    /* Reserved register number.  There should never be a register with such
+       number, this name is needed only for a sanity check in
+      arc_cannot_(fetch|store)_register.  */
+    ARC_RESERVED_REGNUM,
+    /* Long-immediate value.  This is not a physical register - if instruction
+       has register 62 as an operand, then this operand is a literal value
+       stored in the instruction memory right after the instruction itself.
+       This value is required in this enumeration as an architectural number
+       for instruction analysis.  */
+    ARC_LIMM_REGNUM,
     /* Program counter, aligned to 4-bytes, read-only.  */
     ARC_PCL_REGNUM,
     ARC_LAST_CORE_REGNUM = ARC_PCL_REGNUM,
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/5] arc: Add disassembler helper
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
@ 2017-02-14 15:50   ` Eli Zaretskii
  2017-02-17 13:00   ` Pedro Alves
  2017-02-17 13:01   ` [PATCH 4/5] " Pedro Alves
  2 siblings, 0 replies; 23+ messages in thread
From: Eli Zaretskii @ 2017-02-14 15:50 UTC (permalink / raw)
  To: Anton Kolesov; +Cc: gdb-patches, Francois.Bedard

> From: Anton Kolesov <Anton.Kolesov@synopsys.com>
> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>,	Francois Bedard <Francois.Bedard@synopsys.com>
> Date: Tue, 14 Feb 2017 13:01:29 +0300
> 
> Add disassembler helper for GDB, that uses opcodes structure arc_instruction
> and adds convenience functions to handle instruction operands.  This interface
> solves at least those problems with arc_instruction:
> 
>   * Some instructions, like "push_s", have implicit operands which are not
>     directly present in arc_instruction.
>   * Operands of particular meaning, like branch/jump targets, have various
>     locations and meaning depending on type of branch/target.
>   * Access to operand value is abstracted into a separate function, so callee
>     code shouldn't bother if operand value is an immediate value or in a
>     register.
> 
> Testcases included in this commit are fairly limited - they test exclusively
> branch instructions, something that will be used in software single stepping.
> Most of the other parts of this disassembler helper are tested during prologue
> analysis testing.
> 
> gdb/ChangeLog:
> 
> yyyy-mm-dd  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.
> 	* arc-tdep.h (arc_delayed_print_insn): Add function declaration.
> 	* arch/arc-insn.c: New file.
> 	* arch/arc-insn.h: Likewise.
> 
> gdb/doc/ChangeLog:
> 
> yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>
> 
> 	* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".
> 
> gdb/testsuite/ChangeLog:
> 
> yyyy-mm-dd  Anton Kolesov  <anton.kolesov@synopsys.com>
> 
>       * gdb.arch/arc-decode-insn.S: New file.
>       * gdb.arch/arc-decode-insn.exp: Likewise.

OK for the documentation part.

Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/5] arc: Add prologue analysis
  2017-02-14 10:01 ` [PATCH 5/5] arc: Add prologue analysis Anton Kolesov
@ 2017-02-14 15:51   ` Eli Zaretskii
  2017-02-17 13:25   ` Pedro Alves
  1 sibling, 0 replies; 23+ messages in thread
From: Eli Zaretskii @ 2017-02-14 15:51 UTC (permalink / raw)
  To: Anton Kolesov; +Cc: gdb-patches, Francois.Bedard

> From: Anton Kolesov <Anton.Kolesov@synopsys.com>
> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>,	Francois Bedard <Francois.Bedard@synopsys.com>
> Date: Tue, 14 Feb 2017 13:01:30 +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_fprintf_disasm): Likewise.
> 	(arc_disassemble_info): 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.

OK for the documentation part.

Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/5] arc: Set section to ".text" when disassembling
  2017-02-14 10:01 ` [PATCH 2/5] arc: Set section to ".text" when disassembling Anton Kolesov
@ 2017-02-15 22:27   ` Yao Qi
  2017-02-16 16:35     ` Anton Kolesov
  0 siblings, 1 reply; 23+ messages in thread
From: Yao Qi @ 2017-02-15 22:27 UTC (permalink / raw)
  To: Anton Kolesov; +Cc: gdb-patches, Francois Bedard

On Tue, Feb 14, 2017 at 10:01 AM, Anton Kolesov
<Anton.Kolesov@synopsys.com> wrote:
> +
> +  /* ARC disassembler requires that info->section is valid - it will be used to
> +     access program headers of ELF files that is needed to distinguish between
> +     ARC EM and HS (ARC600, ARC700 and ARC v2 can be distinguished by BFD's
> +     `mach` attribute).  Without this information disassembler will default to
> +     ARC EM for ARC v2 targets, and it will not recognize instructions specific
> +     to ARC HS, for example, double-word store and load.  */
> +  if (exec_bfd != NULL)
> +    info->section = bfd_get_section_by_name (exec_bfd, ".text");

info->section should be valid, but why must it be ".text"?  Can I disassembly
instructions in .plt section?  How about using find_pc_section (addr) to get
the right section?  like mep_gdb_print_insn.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH 2/5] arc: Set section to ".text" when disassembling
  2017-02-15 22:27   ` Yao Qi
@ 2017-02-16 16:35     ` Anton Kolesov
  2017-02-17 12:31       ` Pedro Alves
  0 siblings, 1 reply; 23+ messages in thread
From: Anton Kolesov @ 2017-02-16 16:35 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, Francois Bedard

Hi Yao,

> info->section should be valid, but why must it be ".text"?  Can I
> info->disassembly
> instructions in .plt section?  How about using find_pc_section (addr) to get
> the right section?  like mep_gdb_print_insn.

Actual section doesn't matter here because opcodes really need elf header, which is
just obtained through a section. I've used .text simply because it is the section that is
very likely to exist. Using find_pc_section seems to be a much better solution, I wasn't
aware of it. Will change this in second version of the patch.

Anton

> 
> --
> Yao (齐尧)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/5] arc: Set section to ".text" when disassembling
  2017-02-16 16:35     ` Anton Kolesov
@ 2017-02-17 12:31       ` Pedro Alves
  2017-03-15 15:16         ` Anton Kolesov
  0 siblings, 1 reply; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 12:31 UTC (permalink / raw)
  To: Anton Kolesov, Yao Qi; +Cc: gdb-patches, Francois Bedard

On 02/16/2017 04:35 PM, Anton Kolesov wrote:
> Hi Yao,
> 
>> info->section should be valid, but why must it be ".text"?  Can I
>> info->disassembly
>> instructions in .plt section?  How about using find_pc_section (addr) to get
>> the right section?  like mep_gdb_print_insn.
> 
> Actual section doesn't matter here because opcodes really need elf header, which is
> just obtained through a section. I've used .text simply because it is the section that is
> very likely to exist. Using find_pc_section seems to be a much better solution, I wasn't
> aware of it. Will change this in second version of the patch.

And even better approach would be to include the right arch in
the xml target description, in the (existing) <architecture> element, and
then somehow pass that info down to opcodes here...

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/5] arc: Add disassembler helper
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
  2017-02-14 15:50   ` Eli Zaretskii
@ 2017-02-17 13:00   ` Pedro Alves
  2017-03-15 15:17     ` [PATCH 4/5 v2] " Anton Kolesov
  2017-02-17 13:01   ` [PATCH 4/5] " Pedro Alves
  2 siblings, 1 reply; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 13:00 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

On 02/14/2017 10:01 AM, Anton Kolesov wrote:

> diff --git a/gdb/arch/arc-insn.c b/gdb/arch/arc-insn.c
> new file mode 100644
> index 0000000..2dc99a7
> --- /dev/null
> +++ b/gdb/arch/arc-insn.c
> @@ -0,0 +1,275 @@
> +/* ARC disassembler helper.
> +
> +   Copyright 2017 Free Software Foundation, Inc.
> +   Contributed by Synopsys Inc.

Please see:
 https://sourceware.org/gdb/wiki/ContributionChecklist#Attribution

(multiple instances)

> +
> +/* GDB header files.  */
> +#include "defs.h"

Hmm, the original idea of the gdb/arch/ dir was to hold files that could be
used by gdbserver too.  Note how the current files all include common-defs.h 
instead of defs.h.

This file seems to depend on gdb and opcodes, so ... not sure what to
suggest here.  Do you see using this code or parts of it in
gdbserver too in the future?

> +# This tests provides certain degree of testing for arch/arc-insn.c functions,

"These tests"

> +# however it is not a comprehensive testsuite that would go through all
> +# possible ARC instructions - instead this particular test is focused on branch
> +# instructions and whether branch targets are evaluated properly.  Most of the
> +# non-branch aspects of instruction decoder are used during prologue analysis,
> +# so are indirictly tested there.
> +
> +# To maintain separation of test data and test logic, all of the information
> +# about instructions, like if it has delay slot, condition code, branch target
> +# address, is all specified in the test assembly file as a symbols, while this
> +# test case reads those symbols to learn which values are right, then compares
> +# values coming from arc-insn.c with those found in symbols.  More information
> +# about requirements to actual test cases can be found in corresponding
> +# assembly file of this test case (arc-decode-insn.S).
> +
> +if {![istarget "arc*-*-*"]} then {
> +    verbose "Skipping ARC decoder test."
> +    return
> +}
> +
> +standard_testfile .S
> +
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "" ] != "" } {
> +    untested arc-decode-insn.exp
> +    return -1
> +}
> +
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}

Use prepare_for_testing instead of all the above.

> +
> +if ![runto_main] {
> +    fail "Can't run to main"
> +    return 0
> +}
> +
> +# Helper function that reads properties of instruction from the ELF file via
> +# its symbols and then confirms that decoder output aligns to the expected
> +# values.
> +proc test_branch_insn { test_name } {
> +
> +    # Make messages for failed cases more clear, by using hex in them.
> +    set pc [get_hexadecimal_valueof &${test_name}_start -1]
> +
> +    # Calculate instruction length, based on ${test_name}_end symbol.
> +    set end_pc [get_hexadecimal_valueof &${test_name}_end -1]
> +    set length [expr $end_pc - $pc]
> +
> +    set target_address [get_hexadecimal_valueof &${test_name}_target -1]
> +
> +    # Figure out if there is a delay slot, using symbol
> +    # ${test_name}_has_delay_slot.  Note that it should be read via &,
> +    # otherwise it would try to print value at the address specified in
> +    # ${test_name}_has_delay_slot, while a symbol value itself is required.
> +    if { 0 == [get_integer_valueof &${test_name}_has_delay_slot 0] } {
> +	set has_delay_slot 0
> +    } else {
> +	set has_delay_slot 1
> +    }
> +
> +    set cc [get_hexadecimal_valueof &${test_name}_cc 0]
> +
> +    gdb_test_sequence "mt print arc arc-instruction $pc" "" "
> +	length_with_limm=$length
> +	cc=$cc
> +	is_control_flow=1
> +	has_delay_slot=$has_delay_slot
> +	branch_target=$target_address"

The sequence should be a list, with each element being a line.

I think you probably have duplicate test messages.  Please check
with:
https://sourceware.org/gdb/wiki/GDBTestcaseCookbook#Make_sure_test_messages_are_unique

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/5] arc: Add disassembler helper
  2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
  2017-02-14 15:50   ` Eli Zaretskii
  2017-02-17 13:00   ` Pedro Alves
@ 2017-02-17 13:01   ` Pedro Alves
  2 siblings, 0 replies; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 13:01 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

Note, new commands need doc/NEWS entry too, as per contribution checklist:
  https://sourceware.org/gdb/wiki/ContributionChecklist#Documentation

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/5] arc: Add "maintenance print arc" command prefix
  2017-02-14 10:01 ` [PATCH 3/5] arc: Add "maintenance print arc" command prefix Anton Kolesov
@ 2017-02-17 13:02   ` Pedro Alves
  0 siblings, 0 replies; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 13:02 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

LGTM.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/5] arc: Add prologue analysis
  2017-02-14 10:01 ` [PATCH 5/5] arc: Add prologue analysis Anton Kolesov
  2017-02-14 15:51   ` Eli Zaretskii
@ 2017-02-17 13:25   ` Pedro Alves
  2017-03-15 15:18     ` [PATCH 5/5 v2] " Anton Kolesov
  1 sibling, 1 reply; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 13:25 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

On 02/14/2017 10:01 AM, Anton Kolesov wrote:
> +
> +/* Copy of fprintf_disasm from disasm.c.  */
> +
> +static int ATTRIBUTE_PRINTF (2, 3)
> +arc_fprintf_disasm (void *stream, const char *format, ...)
> +{
> +  va_list args;
> +
> +  va_start (args, format);
> +  vfprintf_filtered ((struct ui_file *) stream, format, args);
> +  va_end (args);
> +  /* Something non -ve.  */

This is always the null stream AFAICS,
so why waste time with calling vfprintf_filtered?
See disasm.c:gdb_buffered_insn_length_fprintf, for example.

> +  return 0;
> +}
> +
> +/* Return properly initialized disassemble_info.  */
> +
> +struct disassemble_info
> +arc_disassemble_info (struct gdbarch *gdbarch)

Also, something's odd with the patch split, since this function
was used by an earlier patch.

> +# 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}]
> +    gdb_test "info frame" ".*Saved registers:$regs" \
> +	"saved registers in nested_prologue_inner"
> +    set regs [saved_regs_to_str {r14 r15 blink}]
> +    gdb_test "info frame 1" ".*Saved registers:$regs" \
> +	"saved registers in nested_prologue_outer"
> +

Something's odd with indentation here.

This is missing a NEWS entry for the command.

But otherwise, this looks very good to me.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/5] arc: Align internal regnums with architectural regnums
  2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
                   ` (3 preceding siblings ...)
  2017-02-14 10:01 ` [PATCH 2/5] arc: Set section to ".text" when disassembling Anton Kolesov
@ 2017-02-17 13:26 ` Pedro Alves
  4 siblings, 0 replies; 23+ messages in thread
From: Pedro Alves @ 2017-02-17 13:26 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

OK.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH 2/5] arc: Set section to ".text" when disassembling
  2017-02-17 12:31       ` Pedro Alves
@ 2017-03-15 15:16         ` Anton Kolesov
  0 siblings, 0 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-03-15 15:16 UTC (permalink / raw)
  To: Pedro Alves, Yao Qi; +Cc: gdb-patches, Francois Bedard

> 
> On 02/16/2017 04:35 PM, Anton Kolesov wrote:
> > Hi Yao,
> >
> >> info->section should be valid, but why must it be ".text"?  Can I
> >> info->disassembly
> >> instructions in .plt section?  How about using find_pc_section (addr)
> >> to get the right section?  like mep_gdb_print_insn.
> >
> > Actual section doesn't matter here because opcodes really need elf
> > header, which is just obtained through a section. I've used .text
> > simply because it is the section that is very likely to exist. Using
> > find_pc_section seems to be a much better solution, I wasn't aware of it.
> Will change this in second version of the patch.
> 
> And even better approach would be to include the right arch in the xml
> target description, in the (existing) <architecture> element, and then
> somehow pass that info down to opcodes here...

In our BFD ARC EM and ARC HS are treated as same processor (ARC v2) and
differ via flags in private ELF header, so changing BFD arch will not work.
I'd need to change opcodes, so that it will accept processor as an argument,
instead of always reading ELF header. That would take a while, so I will
remove this patch from the series and will resubmit it later, separately
from prologue analysis.  The only downside is that several architecture
specific test cases will not pass for ARC HS processor for the time being.

Anton

> 
> Thanks,
> Pedro Alves


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 4/5 v2] arc: Add disassembler helper
  2017-02-17 13:00   ` Pedro Alves
@ 2017-03-15 15:17     ` Anton Kolesov
  2017-03-15 15:58       ` Eli Zaretskii
  2017-03-28 13:27       ` Pedro Alves
  0 siblings, 2 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-03-15 15:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

Changes in v2:

  * Add NEWS entry for "maint print arc arc-instruction" command.
  * Fix grammar in the comment.
  * Add arc_disassemble_info function, which was used in this patch, but was
    declared and defined only in the next patch in a sequence.
  * Merge arch/arc-insn.{c,h} files into arc-tdep.{c,h}
  * Improve formatting of output of "maint print arc arc-instruction" command:
    add whitespaces around the "=" symbol.
  * Use "prepare_for" in test cases.
  * Reformat test sequence as a list of string.

---

Add disassembler helper for GDB, that uses opcodes structure arc_instruction
and adds convenience functions to handle instruction operands.  This interface
solves at least those problems with arc_instruction:

  * Some instructions, like "push_s", have implicit operands which are not
    directly present in arc_instruction.
  * Operands of particular meaning, like branch/jump targets, have various
    locations and meaning depending on type of branch/target.
  * Access to operand value is abstracted into a separate function, so callee
    code shouldn't bother if operand value is an immediate value or in a
    register.

Testcases included in this commit are fairly limited - they test exclusively
branch instructions, something that will be used in software single stepping.
Most of the other parts of this disassembler helper are tested during prologue
analysis testing.

gdb/ChangeLog:

yyyy-mm-dd  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.
	  (arc_fprintf_disasm): Likewise.
	  (arc_disassemble_info): Likewise.
	  (arc_insn_get_operand_value): Likewise.
	  (arc_insn_get_operand_value_signed): Likewise.
	  (arc_insn_get_memory_base_reg): Likewise.
	  (arc_insn_get_memory_offset): Likewise.
	  (arc_insn_get_branch_target): Likewise.
	  (arc_insn_dump): Likewise.
	  (arc_insn_get_linear_next_pc): Likewise.
	* arc-tdep.h (arc_delayed_print_insn): Add function declaration.
	  (arc_disassemble_info): Likewise.
	  (arc_insn_get_branch_target): Likewise.
	  (arc_insn_get_linear_next_pc): Likewise.
	* NEWS: Mention new "maint print arc arc-instruction".

gdb/doc/ChangeLog:

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

	* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".

gdb/testsuite/ChangeLog:

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

      * gdb.arch/arc-decode-insn.S: New file.
      * gdb.arch/arc-decode-insn.exp: Likewise.
---
 gdb/NEWS                                   |    3 +
 gdb/arc-tdep.c                             |  315 ++++++++-
 gdb/arc-tdep.h                             |   25 +
 gdb/doc/gdb.texinfo                        |    4 +
 gdb/testsuite/gdb.arch/arc-decode-insn.S   | 1002 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/arc-decode-insn.exp |  132 ++++
 6 files changed, 1480 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.S
 create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index cf58595..754a585 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -94,6 +94,9 @@ show disassembler-options
   The default value is the empty string.  Currently, the only supported
   targets are ARM, PowerPC and S/390.
 
+maint print arc arc-instruction address
+  Print internal disassembler information about instruction at a given address.
+
 *** Changes in GDB 7.12
 
 * GDB and GDBserver now build with a C++ compiler by default.
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 5495f2e..639f7dd 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -32,6 +32,7 @@
 
 /* ARC header files.  */
 #include "opcode/arc.h"
+#include "opcodes/arc-dis.h"
 #include "arc-tdep.h"
 
 /* Standard headers.  */
@@ -116,6 +117,269 @@ static const char *const core_arcompact_register_names[] = {
   "lp_count", "reserved", "limm", "pcl",
 };
 
+/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
+   For relative branch instructions returned value is an offset, not an actual
+   branch target.  */
+
+static ULONGEST
+arc_insn_get_operand_value (const struct arc_instruction &insn,
+			    unsigned int operand_num)
+{
+  switch (insn.operands[operand_num].kind)
+    {
+    case ARC_OPERAND_KIND_LIMM:
+      gdb_assert (insn.limm_p);
+      return insn.limm_value;
+    case ARC_OPERAND_KIND_SHIMM:
+      return insn.operands[operand_num].value;
+    default:
+      /* Value in instruction is a register number.  */
+      struct regcache *regcache = get_current_regcache ();
+      ULONGEST value;
+      regcache_cooked_read_unsigned (regcache,
+				     insn.operands[operand_num].value,
+				     &value);
+      return value;
+    }
+}
+
+/* Like arc_insn_get_operand_value, but returns a signed value.  */
+
+static LONGEST
+arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+				   unsigned int operand_num)
+{
+  switch (insn.operands[operand_num].kind)
+    {
+    case ARC_OPERAND_KIND_LIMM:
+      gdb_assert (insn.limm_p);
+      /* Convert unsigned raw value to signed one.  This assumes 2's
+	 complement arithmetic, but so is the LONG_MIN value from generic
+	 defs.h and that assumption is true for ARC.  */
+      gdb_static_assert (sizeof (insn.limm_value) == sizeof (int));
+      return (((LONGEST) insn.limm_value) ^ INT_MIN) - INT_MIN;
+    case ARC_OPERAND_KIND_SHIMM:
+      /* Sign conversion has been done by binutils.  */
+      return insn.operands[operand_num].value;
+    default:
+      /* Value in instruction is a register number.  */
+      struct regcache *regcache = get_current_regcache ();
+      LONGEST value;
+      regcache_cooked_read_signed (regcache,
+				   insn.operands[operand_num].value,
+				   &value);
+      return value;
+    }
+}
+
+/* Get register with base address of memory operation.  */
+
+int
+arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
+{
+  /* POP_S and PUSH_S have SP as an implicit argument in a disassembler.  */
+  if (insn.insn_class == PUSH || insn.insn_class == POP)
+    return ARC_SP_REGNUM;
+
+  gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+  /* Other instructions all have at least two operands: operand 0 is data,
+     operand 1 is address.  Operand 2 is offset from address.  However, see
+     comment to arc_instruction.operands - in some cases, third operand may be
+     missing, namely if it is 0.  */
+  gdb_assert (insn.operands_count >= 2);
+  return insn.operands[1].value;
+}
+
+/* Get offset of a memory operation INSN.  */
+
+CORE_ADDR
+arc_insn_get_memory_offset (const struct arc_instruction &insn)
+{
+  /* POP_S and PUSH_S have offset as an implicit argument in a
+     disassembler.  */
+  if (insn.insn_class == POP)
+    return 4;
+  else if (insn.insn_class == PUSH)
+    return -4;
+
+  gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+  /* Other instructions all have at least two operands: operand 0 is data,
+     operand 1 is address.  Operand 2 is offset from address.  However, see
+     comment to arc_instruction.operands - in some cases, third operand may be
+     missing, namely if it is 0.  */
+  if (insn.operands_count < 3)
+    return 0;
+
+  CORE_ADDR value = arc_insn_get_operand_value (insn, 2);
+  /* Handle scaling.  */
+  if (insn.writeback_mode == ARC_WRITEBACK_AS)
+    {
+      /* Byte data size is not valid for AS.  Halfword means shift by 1 bit.
+	 Word and double word means shift by 2 bits.  */
+      gdb_assert (insn.data_size_mode != ARC_SCALING_B);
+      if (insn.data_size_mode == ARC_SCALING_H)
+	value <<= 1;
+      else
+	value <<= 2;
+    }
+  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)
+{
+  gdb_assert (insn.is_control_flow);
+
+  /* BI [c]: PC = nextPC + (c << 2).  */
+  if (insn.insn_class == BI)
+    {
+      ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+      return arc_insn_get_linear_next_pc (insn) + (reg_value << 2);
+    }
+  /* BIH [c]: PC = nextPC + (c << 1).  */
+  else if (insn.insn_class == BIH)
+    {
+      ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+      return arc_insn_get_linear_next_pc (insn) + (reg_value << 1);
+    }
+  /* JLI and EI.  */
+  /* JLI and EI depend on optional AUX registers.  Not supported right now.  */
+  else if (insn.insn_class == JLI)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "JLI_S instruction is not supported by the GDB.");
+      return 0;
+    }
+  else if (insn.insn_class == EI)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "EI_S instruction is not supported by the GDB.");
+      return 0;
+    }
+  /* LEAVE_S: PC = BLINK.  */
+  else if (insn.insn_class == LEAVE)
+    {
+      struct regcache *regcache = get_current_regcache ();
+      ULONGEST value;
+      regcache_cooked_read_unsigned (regcache, ARC_BLINK_REGNUM, &value);
+      return value;
+    }
+  /* BBIT0/1, BRcc: PC = currentPC + operand.  */
+  else if (insn.insn_class == BBIT0 || insn.insn_class == BBIT1
+	   || insn.insn_class == BRCC)
+    {
+      /* Most instructions has branch target as their sole argument.  However
+	 conditional brcc/bbit has it as a third operand.  */
+      CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 2);
+
+      /* Offset is relative to the 4-byte aligned address of the current
+	 instruction, hence last two bits should be truncated.  */
+      return pcrel_addr + align_down (insn.address, 4);
+    }
+  /* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand.  */
+  else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
+    {
+      CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 0);
+
+      /* Offset is relative to the 4-byte aligned address of the current
+	 instruction, hence last two bits should be truncated.  */
+      return pcrel_addr + align_down (insn.address, 4);
+    }
+  /* J, Jcc, JL, JLcc: PC = operand.  */
+  else if (insn.insn_class == JUMP)
+    {
+      /* All jumps are single-operand.  */
+      return arc_insn_get_operand_value (insn, 0);
+    }
+
+  /* This is some new and unknown instruction.  */
+  gdb_assert_not_reached ("Unknown branch instruction.");
+}
+
+/* Dump INSN into gdb_stdlog.  */
+
+void
+arc_insn_dump (const struct arc_instruction &insn)
+{
+  struct gdbarch *gdbarch = target_gdbarch ();
+
+  arc_print ("Dumping arc_instruction at %s\n",
+	     paddress (gdbarch, insn.address));
+  arc_print ("\tlength = %u\n", insn.length);
+
+  if (!insn.valid)
+    {
+      arc_print ("\tThis is not a valid ARC instruction.\n");
+      return;
+    }
+
+  arc_print ("\tlength_with_limm = %u\n", insn.length + (insn.limm_p ? 4 : 0));
+  arc_print ("\tcc = 0x%x\n", insn.condition_code);
+  arc_print ("\tinsn_class = %u\n", insn.insn_class);
+  arc_print ("\tis_control_flow = %i\n", insn.is_control_flow);
+  arc_print ("\thas_delay_slot = %i\n", insn.has_delay_slot);
+
+  CORE_ADDR next_pc = arc_insn_get_linear_next_pc (insn);
+  arc_print ("\tlinear_next_pc = %s\n", paddress (gdbarch, next_pc));
+
+  if (insn.is_control_flow)
+    {
+      CORE_ADDR t = arc_insn_get_branch_target (insn);
+      arc_print ("\tbranch_target = %s\n", paddress (gdbarch, t));
+    }
+
+  arc_print ("\tlimm_p = %i\n", insn.limm_p);
+  if (insn.limm_p)
+    arc_print ("\tlimm_value = 0x%08x\n", insn.limm_value);
+
+  if (insn.insn_class == STORE || insn.insn_class == LOAD
+      || insn.insn_class == PUSH || insn.insn_class == POP)
+    {
+      arc_print ("\twriteback_mode = %u\n", insn.writeback_mode);
+      arc_print ("\tdata_size_mode = %u\n", insn.data_size_mode);
+      arc_print ("\tmemory_base_register = %s\n",
+		 gdbarch_register_name (gdbarch,
+					arc_insn_get_memory_base_reg (insn)));
+      /* get_memory_offset returns an unsigned CORE_ADDR, but treat it as a
+	 LONGEST for a nicer representation.  */
+      arc_print ("\taddr_offset = %s\n",
+		 plongest (arc_insn_get_memory_offset (insn)));
+    }
+
+  arc_print ("\toperands_count = %u\n", insn.operands_count);
+  for (unsigned int i = 0; i < insn.operands_count; ++i)
+    {
+      int is_reg = (insn.operands[i].kind == ARC_OPERAND_KIND_REG);
+
+      arc_print ("\toperand[%u] = {\n", i);
+      arc_print ("\t\tis_reg = %i\n", is_reg);
+      if (is_reg)
+	arc_print ("\t\tregister = %s\n",
+		   gdbarch_register_name (gdbarch, insn.operands[i].value));
+      /* Don't know if this value is signed or not, so print both
+	 representations.  This tends to look quite ugly, especially for big
+	 numbers.  */
+      arc_print ("\t\tunsigned value = %s\n",
+		 pulongest (arc_insn_get_operand_value (insn, i)));
+      arc_print ("\t\tsigned value = %s\n",
+		 plongest (arc_insn_get_operand_value_signed (insn, i)));
+      arc_print ("\t}\n");
+    }
+}
+
+CORE_ADDR
+arc_insn_get_linear_next_pc (const struct arc_instruction &insn)
+{
+  /* In ARC long immediate is always 4 bytes.  */
+  return (insn.address + insn.length + (insn.limm_p ? 4 : 0));
+}
+
 /* Implement the "write_pc" gdbarch method.
 
    In ARC PC register is a normal register so in most cases setting PC value
@@ -649,6 +913,30 @@ arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache)
   return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM);
 }
 
+/* Copy of gdb_buffered_insn_length_fprintf from disasm.c.  */
+
+static int ATTRIBUTE_PRINTF (2, 3)
+arc_fprintf_disasm (void *stream, const char *format, ...)
+{
+  return 0;
+}
+
+struct disassemble_info
+arc_disassemble_info (struct gdbarch *gdbarch)
+{
+  struct disassemble_info di;
+  init_disassemble_info (&di, &null_stream, arc_fprintf_disasm);
+  di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+  di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  di.endian = gdbarch_byte_order (gdbarch);
+  di.read_memory_func = [](bfd_vma memaddr, gdb_byte *myaddr,
+			   unsigned int len, struct disassemble_info *info)
+    {
+      return target_read_code (memaddr, myaddr, len);
+    };
+  return di;
+}
+
 /* Implement the "skip_prologue" gdbarch method.
 
    Skip the prologue for the function at PC.  This is done by checking from
@@ -701,7 +989,7 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
    that will not print, or `stream` should be different from standard
    gdb_stdlog.  */
 
-static int
+int
 arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
 {
   int (*print_insn) (bfd_vma, struct disassemble_info *);
@@ -1330,6 +1618,26 @@ maintenance_print_arc_command (char *args, int from_tty)
   cmd_show_list (maintenance_print_arc_list, from_tty, "");
 }
 
+/* This command accepts single argument - address of instruction to
+   disassemble.  */
+
+static void
+dump_arc_instruction_command (char *args, int from_tty)
+{
+  struct value *val;
+  if (args != NULL && strlen (args) > 0)
+    val = evaluate_expression (parse_expression (args).get ());
+  else
+    val = access_value_history (0);
+  record_latest_value (val);
+
+  CORE_ADDR address = value_as_address (val);
+  struct arc_instruction insn;
+  struct disassemble_info di = arc_disassemble_info (target_gdbarch ());
+  arc_insn_decode (address, &di, arc_delayed_print_insn, &insn);
+  arc_insn_dump (insn);
+}
+
 /* Suppress warning from -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_arc_tdep;
 
@@ -1350,6 +1658,11 @@ _initialize_arc_tdep (void)
 		  &maintenance_print_arc_list, "maintenance print arc ", 0,
 		  &maintenanceprintlist);
 
+  add_cmd ("arc-instruction", class_maintenance,
+	   dump_arc_instruction_command,
+	   _("Dump arc_instruction structure for specified address."),
+	   &maintenance_print_arc_list);
+
   /* Debug internals for ARC GDB.  */
   add_setshow_zinteger_cmd ("arc", class_maintenance,
 			    &arc_debug,
diff --git a/gdb/arc-tdep.h b/gdb/arc-tdep.h
index 326f486..1bf1817 100644
--- a/gdb/arc-tdep.h
+++ b/gdb/arc-tdep.h
@@ -123,4 +123,29 @@ arc_mach_is_arcv2 (struct gdbarch *gdbarch)
   return gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arcv2;
 }
 
+/* Function to access ARC disassembler.  Underlying opcodes disassembler will
+   print an instruction into stream specified in the INFO, so if it is
+   undesired, then this stream should be set to some invisible stream, but it
+   can't be set to an actual NULL value - that would cause a crash.  */
+int arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info);
+
+/* Return properly initialized disassemble_info for ARC disassembler - it will
+   not print disassembled instructions to stderr.  */
+
+struct disassemble_info arc_disassemble_info (struct gdbarch *gdbarch);
+
+/* Get branch/jump target address for the INSN.  Note that this function
+   returns branch target and doesn't evaluate if this branch is taken or not.
+   For the indirect jumps value depends in register state, hence can change.
+   It is an error to call this function for a non-branch instruction.  */
+
+CORE_ADDR arc_insn_get_branch_target (const struct arc_instruction &insn);
+
+/* Get address of next instruction after INSN, assuming linear execution (no
+   taken branches).  If instruction has a delay slot, then returned value will
+   point at the instruction in delay slot.  That is - "address of instruction +
+   instruction length with LIMM".  */
+
+CORE_ADDR arc_insn_get_linear_next_pc (const struct arc_instruction &insn);
+
 #endif /* ARC_TDEP_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5cf0f97..344778b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22105,6 +22105,10 @@ messages.
 @kindex show debug arc
 Show the level of ARC specific debugging in operation.
 
+@item maint print arc arc-instruction @var{address}
+@kindex maint print arc arc-instruction
+Print internal disassembler information about instruction at a given address.
+
 @end table
 
 @node ARM
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.S b/gdb/testsuite/gdb.arch/arc-decode-insn.S
new file mode 100644
index 0000000..030f158
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.S
@@ -0,0 +1,1002 @@
+; 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 .text
+.global main
+
+#define TEST_J
+#define TEST_JCC
+#define TEST_JL
+#define TEST_JLCC
+#define TEST_B
+#define TEST_BBIT
+#define TEST_BCC
+#define TEST_BI
+#define TEST_BL
+#define TEST_BRCC
+#define TEST_JLI
+#define TEST_LEAVE_S
+#define TEST_LPCC
+
+; JLI-specific stuff
+#ifdef TEST_JLI
+jli_table:
+    .word 0xdeadbeea
+    .word 0xdeadbeea
+jli_target:
+    .word 0xdeadbeea
+    .word 0xdeadbeea
+
+.set jli_offset, 3
+#endif
+main:
+
+; Each test case requires several symbols to be set, that identify expected
+; parameters of this instruction.  Required symbols:
+;   ${test}_start: symbol points to start of the test
+;   ${test}_end: symbol points to the instruction after the jump/branch
+;      instruction.
+;   ${test}_target: branch target address.
+;   ${test}_has_delay_slot: whether instruction has delay slot.
+;   ${test}_cc: condition code numeric value.
+
+    .set r12_value, 0xdead0000
+    .set blink_value, 0xdead0004
+    .set limm_value, 0xdead0008
+    ; Just an integer
+    .set r4_value, 0xdead000c
+    ; Just an integer
+    .set r5_value, 0xdead0010
+    ; offset index for BI [c]
+    .set r7_value, 4
+    .set u6_value, 0x20
+    .set s12_target, 0x100
+
+    mov r12, @r12_value
+    mov r4, @r4_value
+    mov r5, @r5_value
+    mov r7, @r7_value
+    mov blink, @blink_value
+#ifdef TEST_JLI
+    ; jli_base aux regnum = 0x290
+    sr jli_table, [0x290]
+#endif
+
+start_branch_tests:
+
+#ifdef TEST_J
+
+#define TEST_NAME j_c
+    ; j [c]
+    .set j_c_target, @r4_value
+    .set j_c_has_delay_slot,   0
+    .set j_c_cc, 0
+    j_c_start:
+	j [r4]
+    j_c_end:
+
+    ; j [blink]
+    .set j_blink_target, @blink_value
+    .set j_blink_has_delay_slot,   0
+    .set j_blink_cc, 0
+    mov blink, @j_blink_target
+    j_blink_start:
+	j [blink]
+    j_blink_end:
+
+    ; j limm
+    .set j_limm_target, @limm_value
+    .set j_limm_has_delay_slot,   0
+    .set j_limm_cc, 0
+    j_limm_start:
+	j @j_limm_target
+    j_limm_end:
+
+    ; j u6
+    .set j_u6_target, @u6_value
+    .set j_u6_has_delay_slot,   0
+    .set j_u6_cc, 0
+    j_u6_start:
+	j @j_u6_target
+    j_u6_end:
+
+    ; j s12
+    .set j_s12_target, @s12_target
+    .set j_s12_has_delay_slot,   0
+    .set j_s12_cc, 0
+    j_s12_start:
+	j @j_s12_target
+    j_s12_end:
+
+    ; j.d [c]
+    .set j_d_c_target, @r4_value
+    .set j_d_c_has_delay_slot,   1
+    .set j_d_c_cc, 0
+    j_d_c_start:
+	j.d [r4]
+    j_d_c_end:
+	nop_s
+
+    ; j.d [blink]
+    .set j_d_blink_target, @blink_value
+    .set j_d_blink_has_delay_slot,   1
+    .set j_d_blink_cc, 0
+    j_d_blink_start:
+	j.d [blink]
+    j_d_blink_end:
+	nop_s
+
+    ; j.d u6
+    .set j_d_u6_target, @u6_value
+    .set j_d_u6_has_delay_slot,   1
+    .set j_d_u6_cc, 0
+    j_d_u6_start:
+	j.d @j_d_u6_target
+    j_d_u6_end:
+	nop_s
+
+    ; j.d s12
+    .set j_d_s12_target, @s12_target
+    .set j_d_s12_has_delay_slot,   1
+    .set j_d_s12_cc, 0
+    j_d_s12_start:
+	j.d @j_d_s12_target
+    j_d_s12_end:
+	nop_s
+
+    ; j_s [b]
+    .set j_s_b_target, @r12_value
+    .set j_s_b_has_delay_slot,   0
+    .set j_s_b_cc, 0
+    j_s_b_start:
+	j_s [r12]
+    j_s_b_end:
+
+    ; j_s.d [b]
+    .set j_s_d_b_target, @r12_value
+    .set j_s_d_b_has_delay_slot,   1
+    .set j_s_d_b_cc, 0
+    j_s_d_b_start:
+	j_s.d [r12]
+    j_s_d_b_end:
+	nop_s
+
+    ; j_s [blink]
+    .set j_s_blink_target, @blink_value
+    .set j_s_blink_has_delay_slot,   0
+    .set j_s_blink_cc, 0
+    j_s_blink_start:
+	j_s [blink]
+    j_s_blink_end:
+
+    ; j_s.d [blink]
+    .set j_s_d_blink_target, @blink_value
+    .set j_s_d_blink_has_delay_slot,   1
+    .set j_c_cc, 0
+    j_s_d_blink_start:
+	j_s.d [blink]
+    j_s_d_blink_end:
+	nop_s
+#endif /* TEST_J */
+
+#ifdef TEST_JCC
+    ; jcc [c]
+    .set jcc_c_target, @r4_value
+    .set jcc_c_has_delay_slot,   0
+    .set jcc_c_cc, 1
+    jcc_c_start:
+	jeq [r4]
+    jcc_c_end:
+
+    ; jcc [blink]
+    .set jcc_blink_target, @blink_value
+    .set jcc_blink_has_delay_slot,   0
+    .set jcc_blink_cc, 2
+    jcc_blink_start:
+	jnz [blink]
+    jcc_blink_end:
+
+    ; jcc limm
+    .set jcc_limm_target, @limm_value
+    .set jcc_limm_has_delay_slot,   0
+    .set jcc_limm_cc, 9
+    jcc_limm_start:
+	jgt @jcc_limm_target
+    jcc_limm_end:
+
+    ; jcc u6
+    .set jcc_u6_target, @u6_value
+    .set jcc_u6_has_delay_slot,   0
+    .set jcc_u6_cc, 0xA
+    jcc_u6_start:
+	jge @jcc_u6_target
+    jcc_u6_end:
+
+    ; jcc.d [c]
+    .set jcc_d_c_target, @r4_value
+    .set jcc_d_c_has_delay_slot,   1
+    .set jcc_d_c_cc, 0xB
+    jcc_d_c_start:
+	jlt.d [r4]
+    jcc_d_c_end:
+	nop_s
+
+    ; jcc.d [blink]
+    .set jcc_d_blink_target, @blink_value
+    .set jcc_d_blink_has_delay_slot,   1
+    .set jcc_d_blink_cc, 0xC
+    jcc_d_blink_start:
+	jle.d [blink]
+    jcc_d_blink_end:
+	nop_s
+
+    ; jcc.d u6
+    .set jcc_d_u6_target, @u6_value
+    .set jcc_d_u6_has_delay_slot,   1
+    .set jcc_d_u6_cc, 0xE
+    jcc_d_u6_start:
+	jls.d @jcc_d_u6_target
+    jcc_d_u6_end:
+	nop_s
+
+    ; jeq_s [blink]
+    .set jcc_eq_s_blink_target, @blink_value
+    .set jcc_eq_s_blink_has_delay_slot,   0
+    .set jcc_eq_s_blink_cc, 1
+    jcc_eq_s_blink_start:
+	jeq_s [blink]
+    jcc_eq_s_blink_end:
+
+    ; jne_s [blink]
+    .set jcc_ne_s_blink_target, @blink_value
+    .set jcc_ne_s_blink_has_delay_slot,   0
+    .set jcc_ne_s_blink_cc, 2
+    jcc_ne_s_blink_start:
+	jne_s [blink]
+    jcc_ne_s_blink_end:
+#endif /* TEST_JCC */
+
+#ifdef TEST_JL
+    ; jl [c]
+    .set jl_c_target, @r4_value
+    .set jl_c_has_delay_slot,   0
+    .set jl_c_cc, 0
+    jl_c_start:
+	jl [r4]
+    jl_c_end:
+
+    ; jl limm
+    .set jl_limm_target, @limm_value
+    .set jl_limm_has_delay_slot,   0
+    .set jl_limm_cc, 0
+    jl_limm_start:
+	jl @jl_limm_target
+    jl_limm_end:
+
+    ; jl u6
+    .set jl_u6_target, @u6_value
+    .set jl_u6_has_delay_slot,   0
+    .set jl_u6_cc, 0
+    jl_u6_start:
+	jl @jl_u6_target
+    jl_u6_end:
+
+    ; jl s12
+    .set jl_s12_target, @s12_target
+    .set jl_s12_has_delay_slot,   0
+    .set jl_s12_cc, 0
+    jl_s12_start:
+	jl @jl_s12_target
+    jl_s12_end:
+
+    ; jl.d [c]
+    .set jl_d_c_target, @r4_value
+    .set jl_d_c_has_delay_slot,   1
+    .set jl_d_c_cc, 0
+    jl_d_c_start:
+	jl.d [r4]
+    jl_d_c_end:
+	nop_s
+
+    ; jl.d u6
+    .set jl_d_u6_target, @u6_value
+    .set jl_d_u6_has_delay_slot,   1
+    .set jl_d_u6_cc, 0
+    jl_d_u6_start:
+	jl.d @jl_d_u6_target
+    jl_d_u6_end:
+	nop_s
+
+    ; jl.d s12
+    .set jl_d_s12_target, @s12_target
+    .set jl_d_s12_has_delay_slot,   1
+    .set jl_d_s12_cc, 0
+    jl_d_s12_start:
+	jl.d @jl_d_s12_target
+    jl_d_s12_end:
+	nop_s
+
+    ; jl_s [b]
+    .set jl_s_b_target, @r12_value
+    .set jl_s_b_has_delay_slot,   0
+    .set jl_s_b_cc, 0
+    jl_s_b_start:
+	jl_s [r12]
+    jl_s_b_end:
+
+    ; jl_s.d [b]
+    .set jl_s_d_b_target, @r12_value
+    .set jl_s_d_b_has_delay_slot,   1
+    .set jl_s_d_b_cc, 0
+    jl_s_d_b_start:
+	jl_s.d [r12]
+    jl_s_d_b_end:
+	nop_s
+#endif /* TEST_JL */
+
+#ifdef TEST_JLCC
+    ; jlcc [c]
+    .set jlcc_c_target, @r4_value
+    .set jlcc_c_has_delay_slot,   0
+    .set jlcc_c_cc, 1
+    jlcc_c_start:
+	jleq [r4]
+    jlcc_c_end:
+
+    ; jlcc limm
+    .set jlcc_limm_target, @limm_value
+    .set jlcc_limm_has_delay_slot,   0
+    .set jlcc_limm_cc, 0x9
+    jlcc_limm_start:
+	jlgt @jlcc_limm_target
+    jlcc_limm_end:
+
+    ; jlcc u6
+    .set jlcc_u6_target, @u6_value
+    .set jlcc_u6_has_delay_slot,   0
+    .set jlcc_u6_cc, 0xA
+    jlcc_u6_start:
+	jlge @jlcc_u6_target
+    jlcc_u6_end:
+
+    ; jlcc.d [c]
+    .set jlcc_d_c_target, @r4_value
+    .set jlcc_d_c_has_delay_slot,   1
+    .set jlcc_d_c_cc, 0xB
+    jlcc_d_c_start:
+	jllt.d [r4]
+    jlcc_d_c_end:
+	nop_s
+
+    ; jlcc.d u6
+    .set jlcc_d_u6_target, @u6_value
+    .set jlcc_d_u6_has_delay_slot,   1
+    .set jlcc_d_u6_cc, 0xE
+    jlcc_d_u6_start:
+	jlls.d @jlcc_d_u6_target
+    jlcc_d_u6_end:
+	nop_s
+#endif /* TEST_JLCC */
+
+#ifdef TEST_B
+.Lb_target:
+    ; Artifical nop, so that first b will not branch to itself.
+    nop_s
+    ; b s25
+    .set b_s25_target, @.Lb_target
+    .set b_s25_has_delay_slot,   0
+    .set b_s25_cc, 0
+    b_s25_start:
+	b @b_s25_target
+    b_s25_end:
+
+    ; b.d s25
+    .set b_d_s25_target, @.Lb_target
+    .set b_d_s25_has_delay_slot,   1
+    .set b_d_s25_cc, 0
+    b_d_s25_start:
+	b.d @b_d_s25_target
+    b_d_s25_end:
+	nop_s
+
+    ; b_s s10
+    .set b_s_s10_target, @.Lb_target
+    .set b_s_s10_has_delay_slot,   0
+    .set b_s_s10_cc, 0
+    b_s_s10_start:
+	b_s @b_s_s10_target
+    b_s_s10_end:
+#endif /* TEST_B */
+
+#ifdef TEST_BBIT
+
+; Due to specifics of bbit implementation in assembler, only local symbols can
+; be used as a branch targets for bbit and brcc.
+; bbits and brcc don't have condition code set to anything.
+.Lbbit_target:
+    nop_s
+
+    ; bbit0.nt b,c,s9
+    .set bbit0_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_c_s9_has_delay_slot,   0
+    .set bbit0_nt_b_c_s9_cc, 0
+    bbit0_nt_b_c_s9_start:
+	bbit0.nt r4,r5,@bbit0_nt_b_c_s9_target
+    bbit0_nt_b_c_s9_end:
+
+    ; bbit0.d.nt b,c,s9
+    .set bbit0_d_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit0_d_nt_b_c_s9_has_delay_slot,   1
+    .set bbit0_d_nt_b_c_s9_cc, 0
+    bbit0_d_nt_b_c_s9_start:
+	bbit0.d.nt r4,r5,@.Lbbit_target
+    bbit0_d_nt_b_c_s9_end:
+	nop_s
+
+    ; bbit0.t b,c,s9
+    .set bbit0_t_b_c_s9_target, @.Lbbit_target
+    .set bbit0_t_b_c_s9_has_delay_slot,   0
+    .set bbit0_t_b_c_s9_cc, 0
+    bbit0_t_b_c_s9_start:
+	bbit0.t r4,r5,@.Lbbit_target
+    bbit0_t_b_c_s9_end:
+
+    ; bbit0.d.t b,c,s9
+    .set bbit0_d_t_b_c_s9_target, @.Lbbit_target
+    .set bbit0_d_t_b_c_s9_has_delay_slot,   1
+    .set bbit0_d_t_b_c_s9_cc, 0
+    bbit0_d_t_b_c_s9_start:
+	bbit0.d.t r4,r5,@.Lbbit_target
+    bbit0_d_t_b_c_s9_end:
+	nop_s
+
+    ; bbit0.nt b,u6,s9
+    .set bbit0_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_u6_s9_has_delay_slot,   0
+    .set bbit0_nt_b_u6_s9_cc, 0
+    bbit0_nt_b_u6_s9_start:
+	bbit0.nt r4,u6_value,@.Lbbit_target
+    bbit0_nt_b_u6_s9_end:
+
+    ; bbit0.d.nt b,u6,s9
+    .set bbit0_d_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_d_nt_b_u6_s9_has_delay_slot,   1
+    .set bbit0_d_nt_b_u6_s9_cc, 0
+    bbit0_d_nt_b_u6_s9_start:
+	bbit0.d.nt r4,u6_value,@.Lbbit_target
+    bbit0_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; bbit0.nt b,u6,s9
+    .set bbit0_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_t_b_u6_s9_has_delay_slot,   0
+    .set bbit0_t_b_u6_s9_cc, 0
+    bbit0_t_b_u6_s9_start:
+	bbit0.t r4,u6_value,@.Lbbit_target
+    bbit0_t_b_u6_s9_end:
+
+    ; bbit0.d.nt b,u6,s9
+    .set bbit0_d_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit0_d_t_b_u6_s9_has_delay_slot,   1
+    .set bbit0_d_t_b_u6_s9_cc, 0
+    bbit0_d_t_b_u6_s9_start:
+	bbit0.d.t r4,u6_value,@.Lbbit_target
+    bbit0_d_t_b_u6_s9_end:
+	nop_s
+
+    ; bbit0.nt b,limm,s9
+    .set bbit0_nt_b_limm_s9_target, @.Lbbit_target
+    .set bbit0_nt_b_limm_s9_has_delay_slot,   0
+    .set bbit0_nt_b_limm_s9_cc, 0
+    bbit0_nt_b_limm_s9_start:
+	bbit0.nt r4,limm_value,@.Lbbit_target
+    bbit0_nt_b_limm_s9_end:
+
+    ; bbit0.t b,limm,s9
+    .set bbit0_t_b_limm_s9_target, @.Lbbit_target
+    .set bbit0_t_b_limm_s9_has_delay_slot,   0
+    .set bbit0_t_b_limm_s9_cc, 0
+    bbit0_t_b_limm_s9_start:
+	bbit0.t r4,limm_value,@.Lbbit_target
+    bbit0_t_b_limm_s9_end:
+
+    ; bbit0.nt limm,c,s9
+    .set bbit0_nt_limm_c_s9_target, @.Lbbit_target
+    .set bbit0_nt_limm_c_s9_has_delay_slot,   0
+    .set bbit0_nt_limm_c_s9_cc, 0
+    bbit0_nt_limm_c_s9_start:
+	bbit0.nt limm_value,r4,@.Lbbit_target
+    bbit0_nt_limm_c_s9_end:
+
+    ; bbit0.t limm,c,s9
+    .set bbit0_t_limm_c_s9_target, @.Lbbit_target
+    .set bbit0_t_limm_c_s9_has_delay_slot,   0
+    .set bbit0_t_limm_c_s9_cc, 0
+    bbit0_t_limm_c_s9_start:
+	bbit0.t limm_value,r4,@.Lbbit_target
+    bbit0_t_limm_c_s9_end:
+
+    ; bbit0.nt limm,u6,s9
+    .set bbit0_nt_limm_u6_s9_target, @.Lbbit_target
+    .set bbit0_nt_limm_u6_s9_has_delay_slot,   0
+    .set bbit0_nt_limm_u6_s9_cc, 0
+    bbit0_nt_limm_u6_s9_start:
+	bbit0.nt limm_value,u6_value,@.Lbbit_target
+    bbit0_nt_limm_u6_s9_end:
+
+    ; bbit0.t limm,u6,s9
+    .set bbit0_t_limm_u6_s9_target, @.Lbbit_target
+    .set bbit0_t_limm_u6_s9_has_delay_slot,   0
+    .set bbit0_t_limm_u6_s9_cc, 0
+    bbit0_t_limm_u6_s9_start:
+	bbit0.t limm_value,u6_value,@.Lbbit_target
+    bbit0_t_limm_u6_s9_end:
+
+    ; bbit1.nt b,c,s9
+    .set bbit1_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_c_s9_has_delay_slot,   0
+    .set bbit1_nt_b_c_s9_cc, 0
+    bbit1_nt_b_c_s9_start:
+	bbit1.nt r4,r5,@.Lbbit_target
+    bbit1_nt_b_c_s9_end:
+
+    ; bbit1.d.nt b,c,s9
+    .set bbit1_d_nt_b_c_s9_target, @.Lbbit_target
+    .set bbit1_d_nt_b_c_s9_has_delay_slot,   1
+    .set bbit1_d_nt_b_c_s9_cc, 0
+    bbit1_d_nt_b_c_s9_start:
+	bbit1.d.nt r4,r5,@.Lbbit_target
+    bbit1_d_nt_b_c_s9_end:
+	nop_s
+
+    ; bbit1.t b,c,s9
+    .set bbit1_t_b_c_s9_target, @.Lbbit_target
+    .set bbit1_t_b_c_s9_has_delay_slot,   0
+    .set bbit1_t_b_c_s9_cc, 0
+    bbit1_t_b_c_s9_start:
+	bbit1.t r4,r5,@.Lbbit_target
+    bbit1_t_b_c_s9_end:
+
+    ; bbit1.d.t b,c,s9
+    .set bbit1_d_t_b_c_s9_target, @.Lbbit_target
+    .set bbit1_d_t_b_c_s9_has_delay_slot,   1
+    .set bbit1_d_t_b_c_s9_cc, 0
+    bbit1_d_t_b_c_s9_start:
+	bbit1.d.t r4,r5,@.Lbbit_target
+    bbit1_d_t_b_c_s9_end:
+	nop_s
+
+    ; bbit1.nt b,u6,s9
+    .set bbit1_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_u6_s9_has_delay_slot,   0
+    .set bbit1_nt_b_u6_s9_cc, 0
+    bbit1_nt_b_u6_s9_start:
+	bbit1.nt r4,u6_value,@.Lbbit_target
+    bbit1_nt_b_u6_s9_end:
+
+    ; bbit1.d.nt b,u6,s9
+    .set bbit1_d_nt_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_d_nt_b_u6_s9_has_delay_slot,   1
+    .set bbit1_d_nt_b_u6_s9_cc, 0
+    bbit1_d_nt_b_u6_s9_start:
+	bbit1.d.nt r4,u6_value,@.Lbbit_target
+    bbit1_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; bbit1.nt b,u6,s9
+    .set bbit1_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_t_b_u6_s9_has_delay_slot,   0
+    .set bbit1_t_b_u6_s9_cc, 0
+    bbit1_t_b_u6_s9_start:
+	bbit1.t r4,u6_value,@.Lbbit_target
+    bbit1_t_b_u6_s9_end:
+
+    ; bbit1.d.nt b,u6,s9
+    .set bbit1_d_t_b_u6_s9_target, @.Lbbit_target
+    .set bbit1_d_t_b_u6_s9_has_delay_slot,   1
+    .set bbit1_d_t_b_u6_s9_cc, 0
+    bbit1_d_t_b_u6_s9_start:
+	bbit1.d.t r4,u6_value,@.Lbbit_target
+    bbit1_d_t_b_u6_s9_end:
+	nop_s
+
+    ; bbit1.nt b,limm,s9
+    .set bbit1_nt_b_limm_s9_target, @.Lbbit_target
+    .set bbit1_nt_b_limm_s9_has_delay_slot,   0
+    .set bbit1_nt_b_limm_s9_cc, 0
+    bbit1_nt_b_limm_s9_start:
+	bbit1.nt r4,limm_value,@.Lbbit_target
+    bbit1_nt_b_limm_s9_end:
+
+    ; bbit1.t b,limm,s9
+    .set bbit1_t_b_limm_s9_target, @.Lbbit_target
+    .set bbit1_t_b_limm_s9_has_delay_slot,   0
+    .set bbit1_t_b_limm_s9_cc, 0
+    bbit1_t_b_limm_s9_start:
+	bbit1.t r4,limm_value,@.Lbbit_target
+    bbit1_t_b_limm_s9_end:
+
+    ; bbit1.nt limm,c,s9
+    .set bbit1_nt_limm_c_s9_target, @.Lbbit_target
+    .set bbit1_nt_limm_c_s9_has_delay_slot,   0
+    .set bbit1_nt_limm_c_s9_cc, 0
+    bbit1_nt_limm_c_s9_start:
+	bbit1.nt limm_value,r4,@.Lbbit_target
+    bbit1_nt_limm_c_s9_end:
+
+    ; bbit1.t limm,c,s9
+    .set bbit1_t_limm_c_s9_target, @.Lbbit_target
+    .set bbit1_t_limm_c_s9_has_delay_slot,   0
+    .set bbit1_t_limm_c_s9_cc, 0
+    bbit1_t_limm_c_s9_start:
+	bbit1.t limm_value,r4,@.Lbbit_target
+    bbit1_t_limm_c_s9_end:
+
+    ; bbit1.nt limm,u6,s9
+    .set bbit1_nt_limm_u6_s9_target, @.Lbbit_target
+    .set bbit1_nt_limm_u6_s9_has_delay_slot,   0
+    .set bbit1_nt_limm_u6_s9_cc, 0
+    bbit1_nt_limm_u6_s9_start:
+	bbit1.nt limm_value,u6_value,@.Lbbit_target
+    bbit1_nt_limm_u6_s9_end:
+
+    ; bbit1.t limm,u6,s9
+    .set bbit1_t_limm_u6_s9_target, @.Lbbit_target
+    .set bbit1_t_limm_u6_s9_has_delay_slot,   0
+    .set bbit1_t_limm_u6_s9_cc, 0
+    bbit1_t_limm_u6_s9_start:
+	bbit1.t limm_value,u6_value,@.Lbbit_target
+    bbit1_t_limm_u6_s9_end:
+#endif /* TEST_BBIT */
+
+#ifdef TEST_BCC
+.Lbcc_target:
+    ; bcc s21
+    .set bcc_s21_target, @.Lbcc_target
+    .set bcc_s21_has_delay_slot,   0
+    .set bcc_s21_cc, 1
+    bcc_s21_start:
+	; beq @bcc_s21_target
+	beq @.Lbcc_target
+    bcc_s21_end:
+
+    ; bcc.d s21
+    .set bcc_d_s21_target, @.Lbcc_target
+    .set bcc_d_s21_has_delay_slot,   1
+    .set bcc_d_s21_cc, 1
+    bcc_d_s21_start:
+	beq.d @bcc_d_s21_target
+    bcc_d_s21_end:
+	nop_s
+
+.Lbcc_s_target:
+    ; beq_s s10
+    .set beq_s_s10_target, @.Lbcc_s_target
+    .set beq_s_s10_has_delay_slot,   0
+    .set beq_s_s10_cc, 1
+    beq_s_s10_start:
+	# beq_s.d @beq_s_s10_target
+	beq_s @.Lbcc_s_target
+    beq_s_s10_end:
+
+    ; bne_s s10
+    .set bne_s_s10_target, @.Lbcc_s_target
+    .set bne_s_s10_has_delay_slot,   0
+    .set bne_s_s10_cc, 2
+    bne_s_s10_start:
+	bne_s @bne_s_s10_target
+    bne_s_s10_end:
+
+    ; bgt_s s7
+    .set bgt_s_s7_target, @.Lbcc_s_target
+    .set bgt_s_s7_has_delay_slot,   0
+    .set bgt_s_s7_cc, 0x9
+    bgt_s_s7_start:
+	bgt_s @bgt_s_s7_target
+    bgt_s_s7_end:
+
+    ; bge_s s7
+    .set bge_s_s7_target, @.Lbcc_s_target
+    .set bge_s_s7_has_delay_slot,   0
+    .set bge_s_s7_cc, 0xA
+    bge_s_s7_start:
+	bge_s @bge_s_s7_target
+    bge_s_s7_end:
+
+    ; blt_s s7
+    .set blt_s_s7_target, @.Lbcc_s_target
+    .set blt_s_s7_has_delay_slot,   0
+    .set blt_s_s7_cc, 0xB
+    blt_s_s7_start:
+	blt_s @blt_s_s7_target
+    blt_s_s7_end:
+
+    ; ble_s s7
+    .set ble_s_s7_target, @.Lbcc_s_target
+    .set ble_s_s7_has_delay_slot,   0
+    .set ble_s_s7_cc, 0xC
+    ble_s_s7_start:
+	ble_s @ble_s_s7_target
+    ble_s_s7_end:
+
+    ; bhi_s s7
+    .set bhi_s_s7_target, @.Lbcc_s_target
+    .set bhi_s_s7_has_delay_slot,   0
+    .set bhi_s_s7_cc, 0xD
+    bhi_s_s7_start:
+	bhi_s @bhi_s_s7_target
+    bhi_s_s7_end:
+
+    ; bhs_s s7
+    .set bhs_s_s7_target, @.Lbcc_s_target
+    .set bhs_s_s7_has_delay_slot,   0
+    .set bhs_s_s7_cc, 0x6
+    bhs_s_s7_start:
+	bhs_s @bhs_s_s7_target
+    bhs_s_s7_end:
+
+    ; blo_s s7
+    .set blo_s_s7_target, @.Lbcc_s_target
+    .set blo_s_s7_has_delay_slot,   0
+    .set blo_s_s7_cc, 0x5
+    blo_s_s7_start:
+	blo_s @blo_s_s7_target
+    blo_s_s7_end:
+
+    ; bls_s s7
+    .set bls_s_s7_target, @.Lbcc_s_target
+    .set bls_s_s7_has_delay_slot,   0
+    .set bls_s_s7_cc, 0xE
+    bls_s_s7_start:
+	bls_s @bls_s_s7_target
+    bls_s_s7_end:
+#endif /* TEST_BCC */
+
+#ifdef TEST_BI
+    ; bi [c]
+    .set bi_c_target, @bi_c_end + (@r7_value << 2)
+    .set bi_c_has_delay_slot,   0
+    .set bi_c_cc, 0
+    bi_c_start:
+	bi [r7]
+    bi_c_end:
+
+    ; bih [c]
+    .set bih_c_target, @bih_c_end + (@r7_value << 1)
+    .set bih_c_has_delay_slot,   0
+    .set bih_c_cc, 0
+    bih_c_start:
+	bih [r7]
+    bih_c_end:
+#endif /* TEST_BI */
+
+#ifdef TEST_BL
+.Lbl_target:
+    ; bl s25
+    .set bl_s25_target, @.Lbl_target
+    .set bl_s25_has_delay_slot,   0
+    .set bl_s25_cc, 0
+    bl_s25_start:
+	bl @bl_s25_target
+    bl_s25_end:
+
+    ; bl.d s25
+    .set bl_d_s25_target, @.Lbl_target
+    .set bl_d_s25_has_delay_slot,   1
+    .set bl_d_s25_cc, 0
+    bl_d_s25_start:
+	bl.d @bl_d_s25_target
+    bl_d_s25_end:
+	nop_s
+
+    ; bl_s s13
+    .set bl_s_s13_target, @.Lbl_target
+    .set bl_s_s13_has_delay_slot,   0
+    .set bl_s_s13_cc, 0
+    bl_s_s13_start:
+	bl_s @bl_s_s13_target
+    bl_s_s13_end:
+
+    ; blcc s21
+    .set blcc_s21_target, @.Lbl_target
+    .set blcc_s21_has_delay_slot,   0
+    .set blcc_s21_cc, 1
+    blcc_s21_start:
+	bleq @blcc_s21_target
+    blcc_s21_end:
+
+    ; blcc.d s21
+    .set blcc_d_s21_target, @.Lbl_target
+    .set blcc_d_s21_has_delay_slot,   1
+    .set blcc_d_s21_cc, 2
+    blcc_d_s21_start:
+	blnz.d @blcc_d_s21_target
+    blcc_d_s21_end:
+	nop_s
+#endif /* TEST_BL */
+
+#ifdef TEST_BRCC
+.Lbrcc_target:
+    ; breq.nt b,c,s9
+    .set breq_nt_b_c_s9_target, @.Lbrcc_target
+    .set breq_nt_b_c_s9_has_delay_slot,   0
+    .set breq_nt_b_c_s9_cc, 1
+    breq_nt_b_c_s9_start:
+	breq.nt r4,r5,@.Lbrcc_target
+    breq_nt_b_c_s9_end:
+
+    ; breq.d.nt b,c,s9
+    .set breq_d_nt_b_c_s9_target, @.Lbrcc_target
+    .set breq_d_nt_b_c_s9_has_delay_slot,   1
+    .set breq_d_nt_b_c_s9_cc, 1
+    breq_d_nt_b_c_s9_start:
+	breq.d.nt r4,r5,@.Lbrcc_target
+    breq_d_nt_b_c_s9_end:
+	nop_s
+
+    ; breq.t b,c,s9
+    .set breq_t_b_c_s9_target, @.Lbrcc_target
+    .set breq_t_b_c_s9_has_delay_slot,   0
+    .set breq_t_b_c_s9_cc, 1
+    breq_t_b_c_s9_start:
+	breq.t r4,r5,@.Lbrcc_target
+    breq_t_b_c_s9_end:
+
+    ; breq.d.t b,c,s9
+    .set breq_d_t_b_c_s9_target, @.Lbrcc_target
+    .set breq_d_t_b_c_s9_has_delay_slot,   1
+    .set breq_d_t_b_c_s9_cc, 1
+    breq_d_t_b_c_s9_start:
+	breq.d.t r4,r5,@.Lbrcc_target
+    breq_d_t_b_c_s9_end:
+	nop_s
+
+    ; breq.nt b,u6,s9
+    .set breq_nt_b_u6_s9_target, @.Lbrcc_target
+    .set breq_nt_b_u6_s9_has_delay_slot,   0
+    .set breq_nt_b_u6_s9_cc, 1
+    breq_nt_b_u6_s9_start:
+	breq.nt r4,u6_value,@.Lbrcc_target
+    breq_nt_b_u6_s9_end:
+
+    ; breq.d.nt b,u6,s9
+    .set breq_d_nt_b_u6_s9_target, @.Lbrcc_target
+    .set breq_d_nt_b_u6_s9_has_delay_slot,   1
+    .set breq_d_nt_b_u6_s9_cc, 1
+    breq_d_nt_b_u6_s9_start:
+	breq.d.nt r4,u6_value,@.Lbrcc_target
+    breq_d_nt_b_u6_s9_end:
+	nop_s
+
+    ; breq.nt b,u6,s9
+    .set breq_t_b_u6_s9_target, @.Lbrcc_target
+    .set breq_t_b_u6_s9_has_delay_slot,   0
+    .set breq_t_b_u6_s9_cc, 1
+    breq_t_b_u6_s9_start:
+	breq.t r4,u6_value,@.Lbrcc_target
+    breq_t_b_u6_s9_end:
+
+    ; breq.d.nt b,u6,s9
+    .set breq_d_t_b_u6_s9_target, @.Lbrcc_target
+    .set breq_d_t_b_u6_s9_has_delay_slot,   1
+    .set breq_d_t_b_u6_s9_cc, 1
+    breq_d_t_b_u6_s9_start:
+	breq.d.t r4,u6_value,@.Lbrcc_target
+    breq_d_t_b_u6_s9_end:
+	nop_s
+
+    ; breq.nt b,limm,s9
+    .set breq_nt_b_limm_s9_target, @.Lbrcc_target
+    .set breq_nt_b_limm_s9_has_delay_slot,   0
+    .set breq_nt_b_limm_s9_cc, 1
+    breq_nt_b_limm_s9_start:
+	breq.nt r4,limm_value,@.Lbrcc_target
+    breq_nt_b_limm_s9_end:
+
+    ; breq.t b,limm,s9
+    .set breq_t_b_limm_s9_target, @.Lbrcc_target
+    .set breq_t_b_limm_s9_has_delay_slot,   0
+    .set breq_t_b_limm_s9_cc, 1
+    breq_t_b_limm_s9_start:
+	breq.t r4,limm_value,@.Lbrcc_target
+    breq_t_b_limm_s9_end:
+
+    ; breq.nt limm,c,s9
+    .set breq_nt_limm_c_s9_target, @.Lbrcc_target
+    .set breq_nt_limm_c_s9_has_delay_slot,   0
+    .set breq_nt_limm_c_s9_cc, 1
+    breq_nt_limm_c_s9_start:
+	breq.nt limm_value,r4,@.Lbrcc_target
+    breq_nt_limm_c_s9_end:
+
+    ; breq.t limm,c,s9
+    .set breq_t_limm_c_s9_target, @.Lbrcc_target
+    .set breq_t_limm_c_s9_has_delay_slot,   0
+    .set breq_t_limm_c_s9_cc, 1
+    breq_t_limm_c_s9_start:
+	breq.t limm_value,r4,@.Lbrcc_target
+    breq_t_limm_c_s9_end:
+
+    ; breq.nt limm,u6,s9
+    .set breq_nt_limm_u6_s9_target, @.Lbrcc_target
+    .set breq_nt_limm_u6_s9_has_delay_slot,   0
+    .set breq_nt_limm_u6_s9_cc, 1
+    breq_nt_limm_u6_s9_start:
+	breq.nt limm_value,u6_value,@.Lbrcc_target
+    breq_nt_limm_u6_s9_end:
+
+    ; breq.t limm,u6,s9
+    .set breq_t_limm_u6_s9_target, @.Lbrcc_target
+    .set breq_t_limm_u6_s9_has_delay_slot,   0
+    .set breq_t_limm_u6_s9_cc, 1
+    breq_t_limm_u6_s9_start:
+	breq.t limm_value,u6_value,@.Lbrcc_target
+    breq_t_limm_u6_s9_end:
+
+    ; brne_s b,0,s8
+    .set brne_s_b_0_s8_target, @.Lbrcc_target
+    .set brne_s_b_0_s8_has_delay_slot,   0
+    .set brne_s_b_0_s8_cc, 1
+    brne_s_b_0_s8_start:
+	brne r12,0,@.Lbrcc_target
+    brne_s_b_0_s8_end:
+
+    ; breq_s b,0,s8
+    .set breq_s_b_0_s8_target, @.Lbrcc_target
+    .set breq_s_b_0_s8_has_delay_slot,   0
+    .set breq_s_b_0_s8_cc, 1
+    breq_s_b_0_s8_start:
+	breq r12,0,@.Lbrcc_target
+    breq_s_b_0_s8_end:
+#endif /* TEST_BRCC */
+
+#ifdef TEST_JLI
+    ; jli_s u10
+    .set jli_s_u10_target, @jli_target
+    .set jli_s_u10_has_delay_slot,   0
+    .set jli_s_u10_cc, 0
+    jli_s_u10_start:
+	jli_s jli_offset
+    jli_s_u10_end:
+#endif
+
+#ifdef TEST_LEAVE_S
+    ; leave_s
+    .set leave_s_target, @blink_value
+    .set leave_s_has_delay_slot,   0
+    .set leave_s_cc, 0
+    leave_s_start:
+	; leave_s [r13-gp,fp,blink,pcl]
+	leave_s (14 + 16 + 32 + 64)
+    leave_s_end:
+#endif
+
+#ifdef TEST_LPCC
+    ; lpcc
+    .set lpcc_u7_target, @.Llpcc_end
+    .set lpcc_u7_has_delay_slot,   0
+    .set lpcc_u7_cc, 1
+    lpcc_u7_start:
+	lpeq @lpcc_u7_target
+    lpcc_u7_end:
+	nop
+	nop
+.Llpcc_end:
+#endif
+
+.Lend:
+
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.exp b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
new file mode 100644
index 0000000..516be28
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
@@ -0,0 +1,132 @@
+# 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/>.
+
+
+# These tests provides certain degree of testing for arc_insn functions,
+# however it is not a comprehensive testsuite that would go through all
+# possible ARC instructions - instead this particular test is focused on branch
+# instructions and whether branch targets are evaluated properly.  Most of the
+# non-branch aspects of instruction decoder are used during prologue analysis,
+# so are indirictly tested there.
+
+# To maintain separation of test data and test logic, all of the information
+# about instructions, like if it has delay slot, condition code, branch target
+# address, is all specified in the test assembly file as a symbols, while this
+# test case reads those symbols to learn which values are right, then compares
+# values coming from decoder with those found in symbols.  More information
+# about requirements to actual test cases can be found in corresponding
+# assembly file of this test case (arc-decode-insn.S).
+
+if {![istarget "arc*-*-*"]} then {
+    verbose "Skipping ARC decoder 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
+}
+
+# Helper function that reads properties of instruction from the ELF file via
+# its symbols and then confirms that decoder output aligns to the expected
+# values.
+proc test_branch_insn { test_name } {
+
+    # Make messages for failed cases more clear, by using hex in them.
+    set pc [get_hexadecimal_valueof &${test_name}_start -1]
+
+    # Calculate instruction length, based on ${test_name}_end symbol.
+    set end_pc [get_hexadecimal_valueof &${test_name}_end -1]
+    set length [expr $end_pc - $pc]
+
+    set target_address [get_hexadecimal_valueof &${test_name}_target -1]
+
+    # Figure out if there is a delay slot, using symbol
+    # ${test_name}_has_delay_slot.  Note that it should be read via &,
+    # otherwise it would try to print value at the address specified in
+    # ${test_name}_has_delay_slot, while a symbol value itself is required.
+    if { 0 == [get_integer_valueof &${test_name}_has_delay_slot 0] } {
+	set has_delay_slot 0
+    } else {
+	set has_delay_slot 1
+    }
+
+    set cc [get_hexadecimal_valueof &${test_name}_cc 0]
+
+    # Can't use {} to create a list of items, because variables will not be
+    # evaluated inside the {}.
+    gdb_test_sequence "mt print arc arc-instruction $pc" "" [list \
+	"length_with_limm = $length" \
+	"cc = $cc" \
+	"is_control_flow = 1" \
+	"has_delay_slot = $has_delay_slot" \
+	"branch_target = $target_address"]
+}
+
+set branch_test_list { }
+
+# Add items in the same groups as they can be enabled/disabled in assembly
+# file.
+lappend branch_test_list \
+    j_c j_blink j_limm j_u6 j_s12 j_d_c j_d_blink j_d_u6
+lappend branch_test_list \
+    jcc_c jcc_blink jcc_limm jcc_u6 jcc_d_c jcc_d_blink jcc_d_u6 jcc_eq_s_blink \
+    jcc_ne_s_blink
+lappend branch_test_list \
+    jl_c jl_limm jl_u6 jl_s12 jl_d_c jl_d_u6 jl_d_s12 jl_s_b jl_s_d_b
+lappend branch_test_list \
+    jlcc_c jlcc_limm jlcc_u6 jlcc_d_c jlcc_d_u6
+lappend branch_test_list \
+    b_s25 b_d_s25 b_s_s10
+lappend branch_test_list \
+    bbit0_nt_b_c_s9 bbit0_d_nt_b_c_s9 bbit0_t_b_c_s9 bbit0_d_t_b_c_s9 \
+    bbit0_nt_b_u6_s9 bbit0_d_nt_b_u6_s9 bbit0_t_b_u6_s9 bbit0_d_t_b_u6_s9 \
+    bbit0_nt_b_limm_s9 bbit0_t_b_limm_s9 bbit0_nt_limm_c_s9 bbit0_t_limm_c_s9 \
+    bbit0_nt_limm_u6_s9 bbit0_t_limm_u6_s9 \
+    bbit1_nt_b_c_s9 bbit1_d_nt_b_c_s9 bbit1_t_b_c_s9 bbit1_d_t_b_c_s9 \
+    bbit1_nt_b_u6_s9 bbit1_d_nt_b_u6_s9 bbit1_t_b_u6_s9 bbit1_d_t_b_u6_s9 \
+    bbit1_nt_b_limm_s9 bbit1_t_b_limm_s9 bbit1_nt_limm_c_s9 bbit1_t_limm_c_s9 \
+    bbit1_nt_limm_u6_s9 bbit1_t_limm_u6_s9
+lappend branch_test_list \
+    bcc_s21 bcc_d_s21 \
+    beq_s_s10 bne_s_s10 bgt_s_s7 bge_s_s7 blt_s_s7 ble_s_s7 bhi_s_s7 bhs_s_s7 \
+    blo_s_s7 bls_s_s7
+lappend branch_test_list \
+    bi_c bih_c
+lappend branch_test_list \
+    bl_s25 bl_d_s25 bl_s_s13 \
+    blcc_s21 blcc_d_s21
+lappend branch_test_list \
+     breq_nt_b_c_s9 breq_d_nt_b_c_s9 breq_t_b_c_s9 breq_d_t_b_c_s9 \
+     breq_nt_b_u6_s9 breq_d_nt_b_u6_s9 breq_t_b_u6_s9 breq_d_t_b_u6_s9 \
+     breq_nt_b_limm_s9 breq_t_b_limm_s9 breq_nt_limm_c_s9 breq_t_limm_c_s9 \
+     breq_nt_limm_u6_s9 breq_t_limm_u6_s9
+# lappend branch_test_list jli_s_u10
+lappend branch_test_list leave_s
+lappend branch_test_list lpcc_u7
+
+runto start_branch_tests
+foreach test $branch_test_list {
+    test_branch_insn $test
+}
+
-- 
2.8.3

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 5/5 v2] arc: Add prologue analysis
  2017-02-17 13:25   ` Pedro Alves
@ 2017-03-15 15:18     ` Anton Kolesov
  2017-03-15 15:59       ` Eli Zaretskii
  2017-03-27 14:20       ` Anton Kolesov
  0 siblings, 2 replies; 23+ messages in thread
From: Anton Kolesov @ 2017-03-15 15:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Anton Kolesov, Francois Bedard

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

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/5 v2] arc: Add disassembler helper
  2017-03-15 15:17     ` [PATCH 4/5 v2] " Anton Kolesov
@ 2017-03-15 15:58       ` Eli Zaretskii
  2017-03-28 13:27       ` Pedro Alves
  1 sibling, 0 replies; 23+ messages in thread
From: Eli Zaretskii @ 2017-03-15 15:58 UTC (permalink / raw)
  To: Anton Kolesov; +Cc: gdb-patches, Francois.Bedard

> From: Anton Kolesov <Anton.Kolesov@synopsys.com>
> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>,	Francois Bedard <Francois.Bedard@synopsys.com>
> Date: Wed, 15 Mar 2017 18:17:38 +0300
> 
> Changes in v2:
> 
>   * Add NEWS entry for "maint print arc arc-instruction" command.
>   * Fix grammar in the comment.
>   * Add arc_disassemble_info function, which was used in this patch, but was
>     declared and defined only in the next patch in a sequence.
>   * Merge arch/arc-insn.{c,h} files into arc-tdep.{c,h}
>   * Improve formatting of output of "maint print arc arc-instruction" command:
>     add whitespaces around the "=" symbol.
>   * Use "prepare_for" in test cases.
>   * Reformat test sequence as a list of string.

OK for the documentation parts.

Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/5 v2] arc: Add prologue analysis
  2017-03-15 15:18     ` [PATCH 5/5 v2] " Anton Kolesov
@ 2017-03-15 15:59       ` Eli Zaretskii
  2017-03-27 14:20       ` Anton Kolesov
  1 sibling, 0 replies; 23+ messages in thread
From: Eli Zaretskii @ 2017-03-15 15:59 UTC (permalink / raw)
  To: Anton Kolesov; +Cc: gdb-patches, Francois.Bedard

> From: Anton Kolesov <Anton.Kolesov@synopsys.com>
> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>,	Francois Bedard <Francois.Bedard@synopsys.com>
> Date: Wed, 15 Mar 2017 18:17:54 +0300
> 
> 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.

OK for the documentation part.

Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH 5/5 v2] arc: Add prologue analysis
  2017-03-15 15:18     ` [PATCH 5/5 v2] " Anton Kolesov
  2017-03-15 15:59       ` Eli Zaretskii
@ 2017-03-27 14:20       ` Anton Kolesov
  2017-03-28 13:28         ` Pedro Alves
  1 sibling, 1 reply; 23+ messages in thread
From: Anton Kolesov @ 2017-03-27 14:20 UTC (permalink / raw)
  To: gdb-patches, Pedro Alves (palves@redhat.com); +Cc: Francois Bedard

> -----Original Message-----
> From: Anton Kolesov [mailto:Anton.Kolesov@synopsys.com]
> Sent: Wednesday, March 15, 2017 18:18
> To: gdb-patches@sourceware.org
> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>; Francois Bedard
> <Francois.Bedard@synopsys.com>
> Subject: [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.

Hi Pedro,

Any feedback on two v2 patches from this patch series? 

Anton

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/5 v2] arc: Add disassembler helper
  2017-03-15 15:17     ` [PATCH 4/5 v2] " Anton Kolesov
  2017-03-15 15:58       ` Eli Zaretskii
@ 2017-03-28 13:27       ` Pedro Alves
  1 sibling, 0 replies; 23+ messages in thread
From: Pedro Alves @ 2017-03-28 13:27 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

On 03/15/2017 03:17 PM, Anton Kolesov wrote:
> yyyy-mm-dd  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.
> 	  (arc_fprintf_disasm): Likewise.
> 	  (arc_disassemble_info): Likewise.
> 	  (arc_insn_get_operand_value): Likewise.
> 	  (arc_insn_get_operand_value_signed): Likewise.
> 	  (arc_insn_get_memory_base_reg): Likewise.
> 	  (arc_insn_get_memory_offset): Likewise.
> 	  (arc_insn_get_branch_target): Likewise.
> 	  (arc_insn_dump): Likewise.
> 	  (arc_insn_get_linear_next_pc): Likewise.
> 	* arc-tdep.h (arc_delayed_print_insn): Add function declaration.
> 	  (arc_disassemble_info): Likewise.
> 	  (arc_insn_get_branch_target): Likewise.
> 	  (arc_insn_get_linear_next_pc): Likewise.
> 	* NEWS: Mention new "maint print arc arc-instruction".

Indentation is odd above.  Be sure to only have a leading
tab on the lines that start with "(", not tab + 2 spaces.

> +; This testcase is part of GDB, the GNU debugger.
> +
> +; Copyright 2017 Synopsys Inc.
> +

s/Synopsys/FSF/g?  I noticed it in patch 5/5 too.  Didn't
confirm others.  Please double check the whole series.

Otherwise I have no further comments.  LGTM.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/5 v2] arc: Add prologue analysis
  2017-03-27 14:20       ` Anton Kolesov
@ 2017-03-28 13:28         ` Pedro Alves
  0 siblings, 0 replies; 23+ messages in thread
From: Pedro Alves @ 2017-03-28 13:28 UTC (permalink / raw)
  To: Anton Kolesov, gdb-patches; +Cc: Francois Bedard

On 03/27/2017 03:20 PM, Anton Kolesov wrote:
>> -----Original Message-----
>> From: Anton Kolesov [mailto:Anton.Kolesov@synopsys.com]
>> Sent: Wednesday, March 15, 2017 18:18
>> To: gdb-patches@sourceware.org
>> Cc: Anton Kolesov <Anton.Kolesov@synopsys.com>; Francois Bedard
>> <Francois.Bedard@synopsys.com>
>> Subject: [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.
> 
> Hi Pedro,
> 
> Any feedback on two v2 patches from this patch series? 

v2 only included patches 4 and 5, right?

If so, other than the nits I sent in the reply to patch #4 v2,
I have no further comments.  Fix those and you're good to go.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2017-03-28 13:28 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-14 10:01 [PATCH 1/5] arc: Align internal regnums with architectural regnums Anton Kolesov
2017-02-14 10:01 ` [PATCH 4/5] arc: Add disassembler helper Anton Kolesov
2017-02-14 15:50   ` Eli Zaretskii
2017-02-17 13:00   ` Pedro Alves
2017-03-15 15:17     ` [PATCH 4/5 v2] " Anton Kolesov
2017-03-15 15:58       ` Eli Zaretskii
2017-03-28 13:27       ` Pedro Alves
2017-02-17 13:01   ` [PATCH 4/5] " Pedro Alves
2017-02-14 10:01 ` [PATCH 5/5] arc: Add prologue analysis Anton Kolesov
2017-02-14 15:51   ` Eli Zaretskii
2017-02-17 13:25   ` Pedro Alves
2017-03-15 15:18     ` [PATCH 5/5 v2] " Anton Kolesov
2017-03-15 15:59       ` Eli Zaretskii
2017-03-27 14:20       ` Anton Kolesov
2017-03-28 13:28         ` Pedro Alves
2017-02-14 10:01 ` [PATCH 3/5] arc: Add "maintenance print arc" command prefix Anton Kolesov
2017-02-17 13:02   ` Pedro Alves
2017-02-14 10:01 ` [PATCH 2/5] arc: Set section to ".text" when disassembling Anton Kolesov
2017-02-15 22:27   ` Yao Qi
2017-02-16 16:35     ` Anton Kolesov
2017-02-17 12:31       ` Pedro Alves
2017-03-15 15:16         ` Anton Kolesov
2017-02-17 13:26 ` [PATCH 1/5] arc: Align internal regnums with architectural regnums Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).