public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch, nios2] clean up prologue/epilogue detection code
@ 2014-11-08 23:01 Sandra Loosemore
  2014-11-11  3:33 ` Yao Qi
  0 siblings, 1 reply; 9+ messages in thread
From: Sandra Loosemore @ 2014-11-08 23:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Qi, Yao

[-- Attachment #1: Type: text/plain, Size: 1869 bytes --]

This patch is another installment in the series of Nios II patches aimed 
at removing or isolating hard-wired instruction encodings.  This will 
make the code more extensible for the future addition of new instruction 
set revisions.

In this patch, all explicit matching of opcodes and masks in 
nios2_in_epilogue_p, nios2_analyze_prologue, and nios2_get_net_pc is 
eliminated in favor of invoking the disassembler (via 
nios2_find_opcode_hash) to do that part.  A new set of helper functions 
are introduced to extract instruction operands according to the format 
of the matched instruction opcode.  Future ISA extensions are likely to 
include multiple encodings of some logical operations, such as add or 
subtract, so this organization will allow those details to be 
consolidated in the new helper functions instead of handled inline at 
call sites for those functions.  Also, organizing the analysis by what 
the instructions being examined conceptually do instead of by their 
format and encoding makes it easier to understand.

This patch also fixes an outstanding bug in the code.  Formerly, there 
were assumptions that the prologue and epilogue could only include one 
stack adjustment each.  This used to be true of code emitted by GCC 
except for functions with stack frame too large to be addressed via a 
16-bit offset.  A change made to GCC earlier this year (r208472) to 
correct an ABI conformance issue also means that many functions with 
frame pointers now have an additional stack pointer adjustment too.  In 
lifting the restriction, I made the prologue analyzer a little smarter 
in differentiating valid prologue stack adjustments (decrementing the 
SP, setting the FP from the SP) from those that can only appear in 
epilogues (incrementing the SP, setting the SP from the FP).

Test results on nios2-elf look good.  OK to commit?

-Sandra


[-- Attachment #2: gdb-prologue.log --]
[-- Type: text/x-log, Size: 994 bytes --]

2014-11-08  Sandra Loosemore  <sandra@codesourcery.com>

	gdb/
	* nios2-tdep.c (nios2_fetch_insn): Move up in file.  Disassemble
	the instruction as well as reading it from memory.
	(nios2_match_add): New.
	(nios2_match_sub): New.
	(nios2_match_addi): New.
	(nios2_match_orhi): New.
	(nios2_match_stw): New.
	(nios2_match_ldw): New.
	(nios2_match_rdctl): New.
	(enum branch_condition): New.
	(nios2_match_branch): New.
	(nios2_match_jmpi): New.
	(nios2_match_calli): New.
	(nios2_match_jmpr): New.
	(nios2_match_callr): New.
	(nios2_match_break): New.
	(nios2_match_trap): New.
	(nios2_in_epilogue_p): Rewrite to use new functions.  Permit
	more than one stack adjustment instruction to appear.
	(nios2_analyze_prologue): Likewise.  Detect some additional
	instruction patterns indicating the prologue has ended.
	(nios2_skip_prologue): Delete unused local limit_pc.
	(nios2_breakpoint_from_pc): Make R1-specific encodings explicit.
	(nios2_get_next_pc): Rewrite to use new functions.

	

	

	

[-- Attachment #3: gdb-prologue.patch --]
[-- Type: text/x-patch, Size: 34411 bytes --]

diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
index 1b647ac..816b17b 100644
--- a/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -275,46 +275,398 @@ nios2_init_cache (struct nios2_unwind_cache *cache, CORE_ADDR pc)
   nios2_setup_default (cache);
 }
 
+/* Read and identify an instruction at PC.  If INSNP is non-null,
+   store the instruction word into that location.  Return the opcode
+   pointer or NULL if the memory couldn't be read or disassembled.  */
+static const struct nios2_opcode *
+nios2_fetch_insn (struct gdbarch *gdbarch, CORE_ADDR pc,
+		  unsigned int *insnp)
+{
+  LONGEST memword;
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  unsigned int insn;
+
+  if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE,
+				 gdbarch_byte_order (gdbarch), &memword))
+    return NULL;
+
+  insn = (unsigned int) memword;
+  if (insnp)
+    *insnp = insn;
+  return nios2_find_opcode_hash (insn, mach);
+}
+
+
+/* Match and disassemble an ADD-type instruction, with 3 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_add (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, int *rc)
+{
+  if (op->match == MATCH_R1_ADD || op->match == MATCH_R1_MOV)
+    {
+      *ra = GET_IW_R_A (insn);
+      *rb = GET_IW_R_B (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a SUB-type instruction, with 3 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_sub (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, int *rc)
+{
+  if (op->match == MATCH_R1_SUB)
+    {
+      *ra = GET_IW_R_A (insn);
+      *rb = GET_IW_R_B (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an ADDI-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_addi (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_ADDI)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an ORHI-type instruction, with 2 register operands
+   and one unsigned immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_orhi (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_ORHI)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *uimm = GET_IW_I_IMM16 (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a STW-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_stw (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_STW || op->match == MATCH_R1_STWIO)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a LDW-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_ldw (uint32_t insn,
+		 const struct nios2_opcode *op,
+		 unsigned long mach,
+		 int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_LDW || op->match == MATCH_R1_LDWIO)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a RDCTL instruction, with 2 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+static int
+nios2_match_rdctl (uint32_t insn,
+		   const struct nios2_opcode *op,
+		   unsigned long mach,
+		   int *ra, int *rc)
+{
+  if (op->match == MATCH_R1_RDCTL)
+    {
+      *ra = GET_IW_R_IMM5 (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+
+/* Match and disassemble a branch instruction, with (potentially)
+   2 register operands and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+enum branch_condition {
+  branch_none,
+  branch_eq,
+  branch_ne,
+  branch_ge,
+  branch_geu,
+  branch_lt,
+  branch_ltu
+};
+  
+static int
+nios2_match_branch (uint32_t insn,
+		    const struct nios2_opcode *op,
+		    unsigned long mach,
+		    int *ra, int *rb, int *imm,
+		    enum branch_condition *cond)
+{
+  switch (op->match)
+    {
+    case MATCH_R1_BR:
+      *cond = branch_none;
+      break;
+    case MATCH_R1_BEQ:
+      *cond = branch_eq;
+      break;
+    case MATCH_R1_BNE:
+      *cond = branch_ne;
+      break;
+    case MATCH_R1_BGE:
+      *cond = branch_ge;
+      break;
+    case MATCH_R1_BGEU:
+      *cond = branch_geu;
+      break;
+    case MATCH_R1_BLT:
+      *cond = branch_lt;
+      break;
+    case MATCH_R1_BLTU:
+      *cond = branch_ltu;
+      break;
+    default:
+      return 0;
+    }
+  *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+  *ra = GET_IW_I_A (insn);
+  *rb = GET_IW_I_B (insn);
+  return 1;
+}
+
+/* Match and disassemble a direct jump instruction, with an
+   unsigned operand.  Returns true on success, and fills in the operand
+   pointer.  */
+static int
+nios2_match_jmpi (uint32_t insn,
+		  const struct nios2_opcode *op,
+		  unsigned long mach,
+		  unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_JMPI)
+    {
+      *uimm = GET_IW_J_IMM26 (insn) << 2;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a direct call instruction, with an
+   unsigned operand.  Returns true on success, and fills in the operand
+   pointer.  */
+static int
+nios2_match_calli (uint32_t insn,
+		   const struct nios2_opcode *op,
+		   unsigned long mach,
+		   unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_CALL)
+    {
+      *uimm = GET_IW_J_IMM26 (insn) << 2;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an indirect jump instruction, with a
+   (possibly implicit) register operand.  Returns true on success, and fills
+   in the operand pointer.  */
+
+static int
+nios2_match_jmpr (uint32_t insn,
+		  const struct nios2_opcode *op,
+		  unsigned long mach,
+		  int *ra)
+{
+  switch (op->match)
+    {
+    case MATCH_R1_JMP:
+      *ra = GET_IW_I_A (insn);
+      return 1;
+    case MATCH_R1_RET:
+      *ra = NIOS2_RA_REGNUM;
+      return 1;
+    case MATCH_R1_ERET:
+      *ra = NIOS2_EA_REGNUM;
+      return 1;
+    case MATCH_R1_BRET:
+      *ra = NIOS2_BA_REGNUM;
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Match and disassemble an indirect call instruction, with a register
+   operand.  Returns true on success, and fills in the operand pointer.  */
+
+static int
+nios2_match_callr (uint32_t insn,
+		   const struct nios2_opcode *op,
+		   unsigned long mach,
+		   int *ra)
+{
+  if (op->match == MATCH_R1_CALLR)
+    {
+      *ra = GET_IW_I_A (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a break instruction, with an unsigned operand.
+   Returns true on success, and fills in the operand pointer.  */
+static int
+nios2_match_break (uint32_t insn,
+		  const struct nios2_opcode *op,
+		  unsigned long mach,
+		  unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_BREAK)
+    {
+      *uimm = GET_IW_R_IMM5 (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a trap instruction, with an unsigned operand.
+   Returns true on success, and fills in the operand pointer.  */
+static int
+nios2_match_trap (uint32_t insn,
+		  const struct nios2_opcode *op,
+		  unsigned long mach,
+		  unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_TRAP)
+    {
+      *uimm = GET_IW_R_IMM5 (insn);
+      return 1;
+    }
+  return 0;
+}
+
 /* Helper function to identify when we're in a function epilogue;
    that is, the part of the function from the point at which the
-   stack adjustment is made, to the return or sibcall.  On Nios II,
-   we want to check that the CURRENT_PC is a return-type instruction
-   and that the previous instruction is a stack adjustment.
-   START_PC is the beginning of the function in question.  */
-
+   stack adjustments are made, to the return or sibcall.
+   Note that we may have several stack adjustment instructions, and
+   this function needs to test whether the stack teardown has already
+   started before current_pc, not whether it has completed.  */
 static int
 nios2_in_epilogue_p (struct gdbarch *gdbarch,
 		     CORE_ADDR current_pc,
 		     CORE_ADDR start_pc)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  /* Maximum number of possibly-epilogue instructions to check.
+     Note that this number should not be too large, else we can
+     potentially end up iterating through unmapped memory.  */
+  int ninsns, max_insns = 5;
+  unsigned int insn;
+  const struct nios2_opcode *op = NULL;
+  unsigned int uimm;
+  int imm;
+  int ra, rb, rc;
+  enum branch_condition cond;
+  CORE_ADDR pc;
 
   /* There has to be a previous instruction in the function.  */
-  if (current_pc > start_pc)
-    {
+  if (current_pc <= start_pc)
+    return 0;
 
-      /* Check whether the previous instruction was a stack
-	 adjustment.  */
-      unsigned int insn
-        = read_memory_unsigned_integer (current_pc - NIOS2_OPCODE_SIZE,
-					NIOS2_OPCODE_SIZE, byte_order);
+  /* Find the previous instruction before current_pc.  */
+  op = nios2_fetch_insn (gdbarch, current_pc - NIOS2_OPCODE_SIZE, &insn);
+  if (op == NULL)
+    return 0;
 
-      if ((insn & 0xffc0003c) == 0xdec00004	/* ADDI sp, sp, */
-	  || (insn & 0xffc1ffff) == 0xdec1883a	/* ADD  sp, sp, */
-	  || (insn & 0xffc0003f) == 0xdec00017)	/* LDW  sp, constant(sp) */
-	{
-	  /* Then check if it's followed by a return or a tail
-	     call.  */
-          insn = read_memory_unsigned_integer (current_pc, NIOS2_OPCODE_SIZE,
-					       byte_order);
-
-	  if (insn == 0xf800283a			/* RET */
-	      || insn == 0xe800083a			/* ERET */
-	      || (insn & 0x07ffffff) == 0x0000683a	/* JMP */
-	      || (insn & 0xffc0003f) == 6)		/* BR */
-	    return 1;
-	}
+  /* Beginning with the previous instruction we just located, check whether
+     we are in a sequence of at least one stack adjustment instruction.
+     Possible instructions here include:
+	 ADDI sp, sp, n
+	 ADD sp, sp, rn
+	 LDW sp, n(sp)  */
+  for (ninsns = 0, pc = current_pc; ninsns < max_insns; ninsns++)
+    {
+      int ok = 0;
+      if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
+	ok = (rb == NIOS2_SP_REGNUM);
+      else if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
+	ok = (rc == NIOS2_SP_REGNUM);
+      else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm))
+	ok = (rb == NIOS2_SP_REGNUM);
+      if (!ok)
+	break;
+      /* Fetch the next insn.  */
+      op = nios2_fetch_insn (gdbarch, pc, &insn);
+      if (op == NULL)
+	return 0;
+      pc += op->size;
     }
+
+  /* No stack adjustments found.  */
+  if (ninsns == 0)
+    return 0;
+
+  /* We found a whole lot of stack adjustments.  Be safe, tell GDB that
+     the epilogue stack unwind is in progress even if we didn't see a
+     return yet.  */
+  if (ninsns == max_insns)
+    return 1;
+
+  /* The next instruction following the stack adjustments must be a
+     return, jump, or unconditional branch.  */
+  if (nios2_match_jmpr (insn, op, mach, &ra)
+      || nios2_match_jmpi (insn, op, mach, &uimm)
+      || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+	  && cond == branch_none))
+    return 1;
+
   return 0;
 }
 
@@ -337,31 +689,33 @@ nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
    interested in skipping the prologue.  Otherwise CACHE is filled in
    from the frame information.
 
-   The prologue will consist of the following parts:
-     1) Optional profiling instrumentation.
-        This uses two or three instructions (the last of
-	these might get merged in with the STW which saves RA to the
-	stack).  We interpret these.
+   The prologue may consist of the following parts:
+     1) Profiling instrumentation.  For non-PIC code it looks like:
 	  mov	 r8, ra
 	  call	 mcount
 	  mov	 ra, r8
 
-     2) A stack adjustment or stack which, which will be one of:
-	  addi   sp, sp, -constant
-	or:
-	  movi   r8, constant
-	  sub    sp, sp, r8
-	or
-	  movhi  r8, constant
-	  addi   r8, r8, constant
-	  sub    sp, sp, r8
-	or
+     2) A stack adjustment and save of R4-R7 for varargs functions.
+        This is typically merged with item 3.
+
+     3) A stack adjustment and save of the callee-saved registers;
+	typically an explicit SP decrement and individual register
+	saves.
+
+        There may also be a stack switch here in an exception handler
+	in place of a stack adjustment.  It looks like:
 	  movhi  rx, %hiadj(newstack)
 	  addhi  rx, rx, %lo(newstack)
 	  stw    sp, constant(rx)
 	  mov    sp, rx
 
-     3) An optional stack check, which can take either of these forms:
+     5) A frame pointer save, which can be either a MOV or ADDI.
+
+     6) A further stack pointer adjustment.  This is normally included
+        adjustment in step 4 unless the total adjustment is too large
+	to be done in one step.
+
+     7) A stack overflow check, which can take either of these forms:
 	  bgeu   sp, rx, +8
 	  break  3
 	or
@@ -369,32 +723,18 @@ nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
 	  ...
 	.Lstack_overflow:
 	  break  3
+        If present, this is inserted after the stack pointer adjustments
+	for steps 3, 4, and 6.
 
-     4) Saving any registers which need to be saved.  These will
-        normally just be stored onto the stack:
-	  stw    rx, constant(sp)
-	but in the large frame case will use r8 as an offset back
-	to the cfa:
-	  add    r8, r8, sp
-	  stw    rx, -constant(r8)
-
-	Saving control registers looks slightly different:
-	  rdctl  rx, ctlN
-	  stw    rx, constant(sp)
-
-     5) An optional FP setup, either if the user has requested a
-        frame pointer or if the function calls alloca.
-        This is always:
-	  mov    fp, sp
-
-    The prologue instructions may be interleaved, and the register
-    saves and FP setup can occur in either order.
+    The prologue instructions may be combined or interleaved with other
+    instructions.
 
     To cope with all this variability we decode all the instructions
-    from the start of the prologue until we hit a branch, call or
-    return.  For each of the instructions mentioned in 3, 4 and 5 we
-    handle the limited cases of stores to the stack and operations
-    on constant values.  */
+    from the start of the prologue until we hit an instruction that
+    cannot possibly be a prologue instruction, such as a branch, call,
+    return, or epilogue instruction.  The prologue is considered to end
+    at the last instruction that can definitely be considered a
+    prologue instruction.  */
 
 static CORE_ADDR
 nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
@@ -402,12 +742,13 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 			struct nios2_unwind_cache *cache,
 			struct frame_info *this_frame)
 {
-  /* Maximum lines of prologue to check.
+  /* Maximum number of possibly-prologue instructions to check.
      Note that this number should not be too large, else we can
      potentially end up iterating through unmapped memory.  */
-  CORE_ADDR limit_pc = start_pc + 200;
+  int ninsns, max_insns = 50;
   int regno;
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
 
   /* Does the frame set up the FP register?  */
   int base_reg = 0;
@@ -428,9 +769,7 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
      functions which switch stacks?  */
   CORE_ADDR frame_high;
 
-  /* Is this the end of the prologue?  */
-  int within_prologue = 1;
-
+  /* The last definitely-prologue instruction seen.  */
   CORE_ADDR prologue_end;
 
   /* Is this the innermost function?  */
@@ -444,15 +783,19 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 
   /* Set up the default values of the registers.  */
   nios2_setup_default (cache);
-  prologue_end = start_pc;
 
   /* Find the prologue instructions.  */
-  while (pc < limit_pc && within_prologue)
+  prologue_end = start_pc;
+  for (ninsns = 0; ninsns < max_insns; ninsns++)
     {
       /* Present instruction.  */
       uint32_t insn;
-
-      int prologue_insn = 0;
+      const struct nios2_opcode *op;
+      int ra, rb, rc, imm;
+      unsigned int uimm;
+      unsigned int reglist;
+      int wb, ret;
+      enum branch_condition cond;
 
       if (pc == current_pc)
       {
@@ -466,22 +809,21 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	  fprintf_unfiltered (gdb_stdlog, "*");
       }
 
-      insn = read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-      pc += NIOS2_OPCODE_SIZE;
+      op = nios2_fetch_insn (gdbarch, pc, &insn);
+
+      /* Unknown opcode?  Stop scanning.  */
+      if (op == NULL)
+	break;
+      pc += op->size;
 
       if (nios2_debug)
 	fprintf_unfiltered (gdb_stdlog, "[%08X]", insn);
 
       /* The following instructions can appear in the prologue.  */
 
-      if ((insn & MASK_R1_ADD) == MATCH_R1_ADD)
+      if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
 	{
 	  /* ADD   rc, ra, rb  (also used for MOV) */
-
-	  int ra = GET_IW_R_A (insn);
-	  int rb = GET_IW_R_B (insn);
-	  int rc = GET_IW_R_C (insn);
-
 	  if (rc == NIOS2_SP_REGNUM
 	      && rb == 0
 	      && value[ra].reg == cache->reg_saved[NIOS2_SP_REGNUM].basereg)
@@ -512,6 +854,11 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	      cache->reg_saved[NIOS2_SP_REGNUM].addr = -4;
 	    }
 
+	  else if (rc == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
+	    /* This is setting SP from FP.  This only happens in the
+	       function epilogue.  */
+	    break;
+
 	  else if (rc != 0)
 	    {
 	      if (value[rb].reg == 0)
@@ -522,18 +869,22 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 		value[rc].reg = -1;
 	      value[rc].offset = value[ra].offset + value[rb].offset;
 	    }
-	  prologue_insn = 1;
-	}
 
-      else if ((insn & MASK_R1_SUB) == MATCH_R1_SUB)
+	  /* The add/move is only considered a prologue instruction
+	     if the destination is SP or FP.  */
+	  if (rc == NIOS2_SP_REGNUM || rc == NIOS2_FP_REGNUM)
+	    prologue_end = pc;
+	}
+      
+      else if (nios2_match_sub (insn, op, mach, &ra, &rb, &rc))
 	{
 	  /* SUB   rc, ra, rb */
-
-	  int ra = GET_IW_R_A (insn);
-	  int rb = GET_IW_R_B (insn);
-	  int rc = GET_IW_R_C (insn);
-
-	  if (rc != 0)
+	  if (rc == NIOS2_SP_REGNUM && rb == NIOS2_SP_REGNUM
+	      && value[rc].reg != 0)
+	    /* If we are decrementing the SP by a non-constant amount,
+	       this is alloca, not part of the prologue.  */
+	    break;
+	  else if (rc != 0)
 	    {
 	      if (value[rb].reg == 0)
 		value[rc].reg = value[ra].reg;
@@ -543,184 +894,155 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	    }
 	}
 
-      else if ((insn & MASK_R1_ADDI) == MATCH_R1_ADDI)
+      else if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
 	{
-	  /* ADDI  rb, ra, immed   (also used for MOVI) */
-	  short immed = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
-
-	  /* The first stack adjustment is part of the prologue.
-	     Any subsequent stack adjustments are either down to
-	     alloca or the epilogue so stop analysing when we hit
-	     them.  */
+	  /* ADDI    rb, ra, imm */
+
+	  /* A positive stack adjustment has to be part of the epilogue.  */
 	  if (rb == NIOS2_SP_REGNUM
-	      && (value[rb].offset != 0 || value[ra].reg != NIOS2_SP_REGNUM))
+	      && (imm > 0 || value[ra].reg != NIOS2_SP_REGNUM))
+	    break;
+
+	  /* Likewise restoring SP from FP.  */
+	  else if (rb == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
 	    break;
 
 	  if (rb != 0)
 	    {
 	      value[rb].reg    = value[ra].reg;
-	      value[rb].offset = value[ra].offset + immed;
+	      value[rb].offset = value[ra].offset + imm;
 	    }
 
-	  prologue_insn = 1;
+	  /* The add is only considered a prologue instruction
+	     if the destination is SP or FP.  */
+	  if (rb == NIOS2_SP_REGNUM || rb == NIOS2_FP_REGNUM)
+	    prologue_end = pc;
 	}
 
-      else if ((insn & MASK_R1_ORHI) == MATCH_R1_ORHI)
+      else if (nios2_match_orhi (insn, op, mach, &ra, &rb, &uimm))
 	{
-	  /* ORHI  rb, ra, immed   (also used for MOVHI) */
-	  unsigned int immed = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
-
+	  /* ORHI  rb, ra, uimm   (also used for MOVHI) */
 	  if (rb != 0)
 	    {
   	      value[rb].reg    = (value[ra].reg == 0) ? 0 : -1;
-	      value[rb].offset = value[ra].offset | (immed << 16);
+	      value[rb].offset = value[ra].offset | (uimm << 16);
 	    }
 	}
 
-      else if ((insn & MASK_R1_STW) == MATCH_R1_STW
-	       || (insn & MASK_R1_STWIO) == MATCH_R1_STWIO)
+      else if (nios2_match_stw (insn, op, mach, &ra, &rb, &imm))
         {
-	  /* STW rb, immediate(ra) */
-
-	  short immed16 = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
+	  /* STW rb, imm(ra) */
 
-	  /* Are we storing the original value of a register?
+	  /* Are we storing the original value of a register to the stack?
 	     For exception handlers the value of EA-4 (return
 	     address from interrupts etc) is sometimes stored.  */
 	  int orig = value[rb].reg;
 	  if (orig > 0
 	      && (value[rb].offset == 0
-		  || (orig == NIOS2_EA_REGNUM && value[rb].offset == -4)))
-	    {
-	      /* We are most interested in stores to the stack, but
-		 also take note of stores to other places as they
-		 might be useful later.  */
-	      if ((value[ra].reg == NIOS2_SP_REGNUM
+		  || (orig == NIOS2_EA_REGNUM && value[rb].offset == -4))
+	      && ((value[ra].reg == NIOS2_SP_REGNUM
 		   && cache->reg_saved[orig].basereg != NIOS2_SP_REGNUM)
-		  || cache->reg_saved[orig].basereg == -1)
+		  || cache->reg_saved[orig].basereg == -1))
+	    {
+	      if (pc < current_pc)
 		{
-		  if (pc < current_pc)
-		    {
-		      /* Save off callee saved registers.  */
-		      cache->reg_saved[orig].basereg = value[ra].reg;
-		      cache->reg_saved[orig].addr = value[ra].offset + immed16;
-		    }
-
-		  prologue_insn = 1;
-
-		  if (orig == NIOS2_EA_REGNUM || orig == NIOS2_ESTATUS_REGNUM)
-		    exception_handler = 1;
+		  /* Save off callee saved registers.  */
+		  cache->reg_saved[orig].basereg = value[ra].reg;
+		  cache->reg_saved[orig].addr = value[ra].offset + imm;
 		}
+	      
+	      prologue_end = pc;
+	      
+	      if (orig == NIOS2_EA_REGNUM || orig == NIOS2_ESTATUS_REGNUM)
+		exception_handler = 1;
 	    }
 	  else
-	    /* Non-stack memory writes are not part of the
-	       prologue.  */
-	    within_prologue = 0;
+	    /* Non-stack memory writes cannot appear in the prologue.  */
+	    break;
         }
 
-      else if ((insn & MASK_R1_RDCTL) == MATCH_R1_RDCTL)
+      else if (nios2_match_rdctl (insn, op, mach, &ra, &rc))
 	{
-	  /* RDCTL rC, ctlN */
-	  int rc = GET_IW_R_C (insn);
-	  int n = GET_IW_R_A (insn);
-
+	  /* RDCTL rC, ctlN
+	     This can appear in exception handlers in combination with
+	     a subsequent save to the stack frame.  */
 	  if (rc != 0)
 	    {
-	      value[rc].reg    = NIOS2_STATUS_REGNUM + n;
+	      value[rc].reg    = NIOS2_STATUS_REGNUM + ra;
 	      value[rc].offset = 0;
 	    }
-
-	  prologue_insn = 1;
         }
 
-      else if ((insn & MASK_R1_CALL) == MATCH_R1_CALL
-	       && value[8].reg == NIOS2_RA_REGNUM
-	       && value[8].offset == 0
-	       && value[NIOS2_SP_REGNUM].reg == NIOS2_SP_REGNUM
-	       && value[NIOS2_SP_REGNUM].offset == 0)
+      else if (nios2_match_calli (insn, op, mach, &uimm))
 	{
-	  /* A CALL instruction.  This is treated as a call to mcount
-	     if ra has been stored into r8 beforehand and if it's
-	     before the stack adjust.
-	     Note mcount corrupts r2-r3, r9-r15 & ra.  */
-	  for (i = 2 ; i <= 3 ; i++)
-	    value[i].reg = -1;
-	  for (i = 9 ; i <= 15 ; i++)
-	    value[i].reg = -1;
-	  value[NIOS2_RA_REGNUM].reg = -1;
-
-	  prologue_insn = 1;
-	}
+	  if (value[8].reg == NIOS2_RA_REGNUM
+	      && value[8].offset == 0
+	      && value[NIOS2_SP_REGNUM].reg == NIOS2_SP_REGNUM
+	      && value[NIOS2_SP_REGNUM].offset == 0)
+	    {
+	      /* A CALL instruction.  This is treated as a call to mcount
+		 if ra has been stored into r8 beforehand and if it's
+		 before the stack adjust.
+		 Note mcount corrupts r2-r3, r9-r15 & ra.  */
+	      for (i = 2 ; i <= 3 ; i++)
+		value[i].reg = -1;
+	      for (i = 9 ; i <= 15 ; i++)
+		value[i].reg = -1;
+	      value[NIOS2_RA_REGNUM].reg = -1;
+
+	      prologue_end = pc;
+	    }
 
-      else if ((insn & 0xf83fffff) == 0xd800012e)
-	{
-	   /* BGEU sp, rx, +8
-	      BREAK 3
-	      This instruction sequence is used in stack checking;
-	      we can ignore it.  */
-	  unsigned int next_insn
-	    = read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-
-	  if (next_insn != 0x003da0fa)
-	    within_prologue = 0;
+	  /* Other calls are not part of the prologue.  */
 	  else
-	    pc += NIOS2_OPCODE_SIZE;
-	}
-
-      else if ((insn & 0xf800003f) == 0xd8000036)
-	{
-	   /* BLTU sp, rx, .Lstackoverflow
-	      If the location branched to holds a BREAK 3 instruction
-	      then this is also stack overflow detection.  We can
-	      ignore it.  */
-	  CORE_ADDR target_pc = pc + ((insn & 0x3fffc0) >> 6);
-	  unsigned int target_insn
-	    = read_memory_unsigned_integer (target_pc, NIOS2_OPCODE_SIZE,
-					    byte_order);
-
-	  if (target_insn != 0x003da0fa)
-	    within_prologue = 0;
+	    break;
 	}
 
-      /* Any other instructions are allowed to be moved up into the
-	 prologue.  If we reach a branch, call or return then the
-	 prologue is considered over.  We also consider a second stack
-	 adjustment as terminating the prologue (see above).  */
-      else
+      else if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond))
 	{
-	  switch (GET_IW_R1_OP (insn))
+	  /* Branches not involving a stack overflow check aren't part of
+	     the prologue.  */
+	  if (ra != NIOS2_SP_REGNUM)
+	    break;
+	  else if (cond == branch_geu)
+	    {
+	      /* BGEU sp, rx, +8
+		 BREAK 3
+		 This instruction sequence is used in stack checking;
+		 we can ignore it.  */
+	      unsigned int next_insn;
+	      const struct nios2_opcode *next_op
+		= nios2_fetch_insn (gdbarch, pc, &next_insn);
+	      if (next_op != NULL
+		  && nios2_match_break (next_insn, op, mach, &uimm))
+		pc += next_op->size;
+	      else
+		break;
+	    }
+	  else if (cond == branch_ltu)
 	    {
-	    case R1_OP_BEQ:
-	    case R1_OP_BGE:
-	    case R1_OP_BGEU:
-	    case R1_OP_BLT:
-	    case R1_OP_BLTU:
-	    case R1_OP_BNE:
-	    case R1_OP_BR:
-	    case R1_OP_CALL:
-	      within_prologue = 0;
-	      break;
-	    case R1_OP_OPX:
-	      if (GET_IW_R_OPX (insn) == R1_OPX_RET
-		  || GET_IW_R_OPX (insn) == R1_OPX_ERET
-		  || GET_IW_R_OPX (insn) == R1_OPX_BRET
-		  || GET_IW_R_OPX (insn) == R1_OPX_CALLR
-		  || GET_IW_R_OPX (insn) == R1_OPX_JMP)
-		within_prologue = 0;
-	      break;
-	    default:
-	      break;
+	      /* BLTU sp, rx, .Lstackoverflow
+		 If the location branched to holds a BREAK 3 instruction
+		 then this is also stack overflow detection.  */
+	      unsigned int next_insn;
+	      const struct nios2_opcode *next_op
+		= nios2_fetch_insn (gdbarch, pc + imm, &next_insn);
+	      if (next_op != NULL
+		  && nios2_match_break (next_insn, op, mach, &uimm))
+		;
+	      else
+		break;
 	    }
+	  else
+	    break;
 	}
 
-      if (prologue_insn)
-	prologue_end = pc;
+      /* All other calls or jumps (including returns) terminate 
+	 the prologue.  */
+      else if (nios2_match_callr (insn, op, mach, &ra)
+	       || nios2_match_jmpr (insn, op, mach, &ra)
+	       || nios2_match_jmpi (insn, op, mach, &uimm))
+	break;
     }
 
   /* If THIS_FRAME is NULL, we are being called from skip_prologue
@@ -858,7 +1180,6 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 static CORE_ADDR
 nios2_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
 {
-  CORE_ADDR limit_pc;
   CORE_ADDR func_addr;
 
   struct nios2_unwind_cache cache;
@@ -886,21 +1207,19 @@ static const gdb_byte*
 nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
 			  int *bp_size)
 {
-  /* break encoding: 31->27  26->22  21->17  16->11 10->6 5->0 */
-  /*                 00000   00000   0x1d    0x2d   11111 0x3a */
-  /*                 00000   00000   11101   101101 11111 111010 */
-  /* In bytes:       00000000 00111011 01101111 11111010 */
-  /*                 0x0       0x3b    0x6f     0xfa */
-  static const gdb_byte breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0};
-  static const gdb_byte breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa};
-
-  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-
-  *bp_size = 4;
-  if (gdbarch_byte_order_for_code (gdbarch) == BFD_ENDIAN_BIG)
-    return breakpoint_be;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+
+  /* R1 break encoding:
+     ((0x1e << 17) | (0x34 << 11) | (0x1f << 6) | (0x3a << 0))
+     0x003da7fa */
+  static const gdb_byte r1_breakpoint_le[] = {0xfa, 0xa7, 0x3d, 0x0};
+  static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3d, 0xa7, 0xfa};
+  *bp_size = NIOS2_OPCODE_SIZE;
+  if (byte_order == BFD_ENDIAN_BIG)
+    return r1_breakpoint_be;
   else
-    return breakpoint_le;
+    return r1_breakpoint_le;
 }
 
 /* Implement the print_insn gdbarch method.  */
@@ -1256,15 +1575,7 @@ static const struct frame_unwind nios2_stub_frame_unwind =
   nios2_stub_frame_sniffer
 };
 
-/* Helper function to read an instruction at PC.  */
-
-static unsigned long
-nios2_fetch_instruction (struct gdbarch *gdbarch, CORE_ADDR pc)
-{
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
-  return read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-}
 
 /* Determine where to set a single step breakpoint while considering
    branch prediction.  */
@@ -1274,88 +1585,79 @@ nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  unsigned long inst;
-  int op;
-  int imm16;
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  unsigned int insn;
+  const struct nios2_opcode *op = nios2_fetch_insn (gdbarch, pc, &insn);
   int ra;
   int rb;
-  int ras;
-  int rbs;
-  unsigned int rau;
-  unsigned int rbu;
-
-  inst = nios2_fetch_instruction (gdbarch, pc);
-  pc += NIOS2_OPCODE_SIZE;
-
-  imm16 = (short) GET_IW_I_IMM16 (inst);
-  ra = GET_IW_I_A (inst);
-  rb = GET_IW_I_B (inst);
-  ras = get_frame_register_signed (frame, ra);
-  rbs = get_frame_register_signed (frame, rb);
-  rau = get_frame_register_unsigned (frame, ra);
-  rbu = get_frame_register_unsigned (frame, rb);
-
-  switch (GET_IW_R1_OP (inst))
+  int imm;
+  unsigned int uimm;
+  int wb, ret;
+  enum branch_condition cond;
+
+  /* Do something stupid if we can't disassemble the insn at pc.  */
+  if (op == NULL)
+    return pc + NIOS2_OPCODE_SIZE;
+    
+  if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond))
     {
-    case R1_OP_BEQ:
-      if (ras == rbs)
-	pc += imm16;
-      break;
-
-    case R1_OP_BGE:
-      if (ras >= rbs)
-        pc += imm16;
-      break;
-
-    case R1_OP_BGEU:
-      if (rau >= rbu)
-        pc += imm16;
-      break;
-
-    case R1_OP_BLT:
-      if (ras < rbs)
-        pc += imm16;
-      break;
-
-    case R1_OP_BLTU:
-      if (rau < rbu)
-        pc += imm16;
-      break;
-
-    case R1_OP_BNE:
-      if (ras != rbs)
-        pc += imm16;
-      break;
-
-    case R1_OP_BR:
-      pc += imm16;
-      break;
+      int ras = get_frame_register_signed (frame, ra);
+      int rbs = get_frame_register_signed (frame, rb);
+      unsigned int rau = get_frame_register_unsigned (frame, ra);
+      unsigned int rbu = get_frame_register_unsigned (frame, rb);
 
-    case R1_OP_JMPI:
-    case R1_OP_CALL:
-      pc = (pc & 0xf0000000) | (GET_IW_J_IMM26 (inst) << 2);
-      break;
-
-    case R1_OP_OPX:
-      switch (GET_IW_R_OPX (inst))
+      pc += op->size;
+      switch (cond)
 	{
-	case R1_OPX_JMP:
-	case R1_OPX_CALLR:
-	case R1_OPX_RET:
-	  pc = ras;
+	case branch_none:
+	  pc += imm;
+	  break;
+	case branch_eq:
+	  if (ras == rbs)
+	    pc += imm;
+	  break;
+	case branch_ne:
+	  if (ras != rbs)
+	    pc += imm;
+	  break;
+	case branch_ge:
+	  if (ras >= rbs)
+	    pc += imm;
+	  break;
+	case branch_geu:
+	  if (rau >= rbu)
+	    pc += imm;
+	  break;
+	case branch_lt:
+	  if (ras < rbs)
+	    pc += imm;
+	  break;
+	case branch_ltu:
+	  if (rau < rbu)
+	    pc += imm;
 	  break;
-
-	case R1_OPX_TRAP:
-	  if (tdep->syscall_next_pc != NULL)
-	    return tdep->syscall_next_pc (frame);
-
 	default:
 	  break;
 	}
-      break;
-    default:
-      break;
     }
+
+  else if (nios2_match_jmpi (insn, op, mach, &uimm)
+	   || nios2_match_calli (insn, op, mach, &uimm))
+    pc = (pc & 0xf0000000) | uimm;
+
+  else if (nios2_match_jmpr (insn, op, mach, &ra)
+	   || nios2_match_callr (insn, op, mach, &ra))
+    pc = get_frame_register_unsigned (frame, ra);
+
+  else if (nios2_match_trap (insn, op, mach, &uimm))
+    {
+      if (tdep->syscall_next_pc != NULL)
+	return tdep->syscall_next_pc (frame);
+    }
+
+  else
+    pc += op->size;
+
   return pc;
 }
 

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

* Re: [patch, nios2] clean up prologue/epilogue detection code
  2014-11-08 23:01 [patch, nios2] clean up prologue/epilogue detection code Sandra Loosemore
@ 2014-11-11  3:33 ` Yao Qi
  2014-11-24 23:52   ` Sandra Loosemore
  0 siblings, 1 reply; 9+ messages in thread
From: Yao Qi @ 2014-11-11  3:33 UTC (permalink / raw)
  To: Sandra Loosemore; +Cc: gdb-patches, Qi, Yao

Sandra Loosemore <sandra@codesourcery.com> writes:

Hi, Sandra,

> In this patch, all explicit matching of opcodes and masks in
> nios2_in_epilogue_p, nios2_analyze_prologue, and nios2_get_net_pc is
> eliminated in favor of invoking the disassembler (via
> nios2_find_opcode_hash) to do that part.  A new set of helper
> functions are introduced to extract instruction operands according to
> the format of the matched instruction opcode.  Future ISA extensions
> are likely to include multiple encodings of some logical operations,
> such as add or subtract, so this organization will allow those details
> to be consolidated in the new helper functions instead of handled
> inline at call sites for those functions.  Also, organizing the
> analysis by what the instructions being examined conceptually do
> instead of by their format and encoding makes it easier to understand.

Great, these helpers look very clear to me.

>
> This patch also fixes an outstanding bug in the code.  Formerly, there
> were assumptions that the prologue and epilogue could only include one
> stack adjustment each.  This used to be true of code emitted by GCC
> except for functions with stack frame too large to be addressed via a
> 16-bit offset.  A change made to GCC earlier this year (r208472) to
> correct an ABI conformance issue also means that many functions with
> frame pointers now have an additional stack pointer adjustment too.
> In lifting the restriction, I made the prologue analyzer a little
> smarter in differentiating valid prologue stack adjustments
> (decrementing the SP, setting the FP from the SP) from those that can
> only appear in epilogues (incrementing the SP, setting the SP from the
> FP).

These fixes should be in separate patches.  I'd like to see this patch
is split to a series which includes the following patches,

 1. add new helpers and rewrite existing functions with these new
 helpers,
 2. permit more than one stack adjustment instruction to appear in
 epilogue
 3. detect some additional prologue instruction patterns,

> diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
> index 1b647ac..816b17b 100644
> --- a/gdb/nios2-tdep.c
> +++ b/gdb/nios2-tdep.c
> @@ -275,46 +275,398 @@ nios2_init_cache (struct nios2_unwind_cache *cache, CORE_ADDR pc)
>    nios2_setup_default (cache);
>  }
>  
> +/* Read and identify an instruction at PC.  If INSNP is non-null,
> +   store the instruction word into that location.  Return the opcode
> +   pointer or NULL if the memory couldn't be read or disassembled.  */

There should be an empty line between the end of comment and the function.

> +static const struct nios2_opcode *
> +nios2_fetch_insn (struct gdbarch *gdbarch, CORE_ADDR pc,
> +		  unsigned int *insnp)
> +{
> +  LONGEST memword;
> +  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
> +  unsigned int insn;
> +
> +  if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE,
> +				 gdbarch_byte_order (gdbarch), &memword))
> +    return NULL;
> +
> +  insn = (unsigned int) memword;
> +  if (insnp)
> +    *insnp = insn;
> +  return nios2_find_opcode_hash (insn, mach);
> +}
> +
> +
> +/* Match and disassemble an ADD-type instruction, with 3 register operands.
> +   Returns true on success, and fills in the operand pointers.  */
> +static int
> +nios2_match_add (uint32_t insn,
> +		 const struct nios2_opcode *op,
> +		 unsigned long mach,
> +		 int *ra, int *rb, int *rc)

Is there any reason you put these parameters in different lines?  We can
place them on the same line like:

static int
nios2_match_add (uint32_t insn, const struct nios2_opcode *op,
		 unsigned long mach, int *ra, int *rb, int *rc)

then the function can be shorter.

> +{
> +  if (op->match == MATCH_R1_ADD || op->match == MATCH_R1_MOV)
> +    {
> +      *ra = GET_IW_R_A (insn);
> +      *rb = GET_IW_R_B (insn);
> +      *rc = GET_IW_R_C (insn);
> +      return 1;
> +    }
> +  return 0;
> +}
> +


> +
>  /* Helper function to identify when we're in a function epilogue;
>     that is, the part of the function from the point at which the
> -   stack adjustment is made, to the return or sibcall.  On Nios II,
> -   we want to check that the CURRENT_PC is a return-type instruction
> -   and that the previous instruction is a stack adjustment.
> -   START_PC is the beginning of the function in question.  */
> -
> +   stack adjustments are made, to the return or sibcall.
> +   Note that we may have several stack adjustment instructions, and
> +   this function needs to test whether the stack teardown has already
> +   started before current_pc, not whether it has completed.  */
>  static int
>  nios2_in_epilogue_p (struct gdbarch *gdbarch,
>  		     CORE_ADDR current_pc,
>  		     CORE_ADDR start_pc)
>  {
> -  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
> +  /* Maximum number of possibly-epilogue instructions to check.
> +     Note that this number should not be too large, else we can
> +     potentially end up iterating through unmapped memory.  */
> +  int ninsns, max_insns = 5;
> +  unsigned int insn;
> +  const struct nios2_opcode *op = NULL;
> +  unsigned int uimm;
> +  int imm;
> +  int ra, rb, rc;
> +  enum branch_condition cond;
> +  CORE_ADDR pc;
>  
>    /* There has to be a previous instruction in the function.  */
> -  if (current_pc > start_pc)
> -    {
> +  if (current_pc <= start_pc)
> +    return 0;
>  
> -      /* Check whether the previous instruction was a stack
> -	 adjustment.  */
> -      unsigned int insn
> -        = read_memory_unsigned_integer (current_pc - NIOS2_OPCODE_SIZE,
> -					NIOS2_OPCODE_SIZE, byte_order);
> +  /* Find the previous instruction before current_pc.  */
> +  op = nios2_fetch_insn (gdbarch, current_pc - NIOS2_OPCODE_SIZE, &insn);
> +  if (op == NULL)
> +    return 0;
>  
> -      if ((insn & 0xffc0003c) == 0xdec00004	/* ADDI sp, sp, */
> -	  || (insn & 0xffc1ffff) == 0xdec1883a	/* ADD  sp, sp, */
> -	  || (insn & 0xffc0003f) == 0xdec00017)	/* LDW  sp, constant(sp) */
> -	{
> -	  /* Then check if it's followed by a return or a tail
> -	     call.  */
> -          insn = read_memory_unsigned_integer (current_pc, NIOS2_OPCODE_SIZE,
> -					       byte_order);
> -
> -	  if (insn == 0xf800283a			/* RET */
> -	      || insn == 0xe800083a			/* ERET */
> -	      || (insn & 0x07ffffff) == 0x0000683a	/* JMP */
> -	      || (insn & 0xffc0003f) == 6)		/* BR */
> -	    return 1;
> -	}
> +  /* Beginning with the previous instruction we just located, check whether
> +     we are in a sequence of at least one stack adjustment instruction.
> +     Possible instructions here include:
> +	 ADDI sp, sp, n
> +	 ADD sp, sp, rn
> +	 LDW sp, n(sp)  */
> +  for (ninsns = 0, pc = current_pc; ninsns < max_insns; ninsns++)
> +    {
> +      int ok = 0;

An empty line is needed between declaration and statement.

> +      if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
> +	ok = (rb == NIOS2_SP_REGNUM);
> +      else if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
> +	ok = (rc == NIOS2_SP_REGNUM);
> +      else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm))
> +	ok = (rb == NIOS2_SP_REGNUM);
> +      if (!ok)
> +	break;
> +      /* Fetch the next insn.  */
> +      op = nios2_fetch_insn (gdbarch, pc, &insn);
> +      if (op == NULL)
> +	return 0;
> +      pc += op->size;
>      }
> +
> +  /* No stack adjustments found.  */
> +  if (ninsns == 0)
> +    return 0;
> +
> +  /* We found a whole lot of stack adjustments.  Be safe, tell GDB that
> +     the epilogue stack unwind is in progress even if we didn't see a
> +     return yet.  */
> +  if (ninsns == max_insns)
> +    return 1;

I am confused by the comments and code.  Infer from your code above that
GCC emits arbitrary number of sp-adjusting continuous instructions in
epilogue, is that true?

> +
> +  /* The next instruction following the stack adjustments must be a
> +     return, jump, or unconditional branch.  */
> +  if (nios2_match_jmpr (insn, op, mach, &ra)
> +      || nios2_match_jmpi (insn, op, mach, &uimm)
> +      || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
> +	  && cond == branch_none))
> +    return 1;
> +
>    return 0;
>  }
>  

Looks the epilogue detection isn't precise nor accurate.  Supposing we
have an epilogue like this below,

   1. ADDI sp, sp, n
   2. RET

if pc points at insn #1, nios2_in_epilogue_p returns true, which isn't
precise, because the stack frame isn't destroyed at the moment pc points
at insn #1.  gdbarch in_function_epilogue_p's name is misleading, and
better to be renamed to stack_frame_destroyed_p (it's on my todo list).

if pc points at insn #2, nios2_in_epilogue_p returns false, which isn't
accurate.

Probably, we need do the forward scan first, skipping arbitrary number of
sp-adjusting instructions until a return or jump, and then do a backward
scan to match a sp-adjusting instruction.  In this case, return true,
otherwise, return false.  Hopefully, this fixes some sw watchpoint
related test fails.

>  nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
>  			  int *bp_size)
>  {
> -  /* break encoding: 31->27  26->22  21->17  16->11 10->6 5->0 */
> -  /*                 00000   00000   0x1d    0x2d   11111 0x3a */
> -  /*                 00000   00000   11101   101101 11111 111010 */
> -  /* In bytes:       00000000 00111011 01101111 11111010 */
> -  /*                 0x0       0x3b    0x6f     0xfa */
> -  static const gdb_byte breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0};
> -  static const gdb_byte breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa};
> -
> -  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
> -
> -  *bp_size = 4;
> -  if (gdbarch_byte_order_for_code (gdbarch) == BFD_ENDIAN_BIG)
> -    return breakpoint_be;
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

Why do we change from byte_order_for_code to byte_order?

> +  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
> +
> +  /* R1 break encoding:
> +     ((0x1e << 17) | (0x34 << 11) | (0x1f << 6) | (0x3a << 0))
> +     0x003da7fa */
> +  static const gdb_byte r1_breakpoint_le[] = {0xfa, 0xa7, 0x3d, 0x0};
> +  static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3d, 0xa7, 0xfa};
> +  *bp_size = NIOS2_OPCODE_SIZE;
> +  if (byte_order == BFD_ENDIAN_BIG)
> +    return r1_breakpoint_be;
>    else
> -    return breakpoint_le;
> +    return r1_breakpoint_le;
>  }
>  

-- 
Yao (齐尧)

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

* Re: [patch, nios2] clean up prologue/epilogue detection code
  2014-11-11  3:33 ` Yao Qi
@ 2014-11-24 23:52   ` Sandra Loosemore
  2014-11-25  0:03     ` [3/3 patch, nios2] clean up prologue/epilogue detection code, v2 Sandra Loosemore
                       ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Sandra Loosemore @ 2014-11-24 23:52 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 11/10/2014 08:33 PM, Yao Qi wrote:

> These fixes should be in separate patches.  I'd like to see this patch
> is split to a series which includes the following patches,
>
>   1. add new helpers and rewrite existing functions with these new
>   helpers,
>   2. permit more than one stack adjustment instruction to appear in
>   epilogue
>   3. detect some additional prologue instruction patterns,

Hmmm, OK (although this required rewriting code for part 1 that is 
promptly going to get thrown away again with parts 2 and 3).

> There should be an empty line between the end of comment and the function.

Fixed throughout the patch.

>> +
>> +/* Match and disassemble an ADD-type instruction, with 3 register operands.
>> +   Returns true on success, and fills in the operand pointers.  */
>> +static int
>> +nios2_match_add (uint32_t insn,
>> +		 const struct nios2_opcode *op,
>> +		 unsigned long mach,
>> +		 int *ra, int *rb, int *rc)
>
> Is there any reason you put these parameters in different lines?  We can
> place them on the same line like:
>
> static int
> nios2_match_add (uint32_t insn, const struct nios2_opcode *op,
> 		 unsigned long mach, int *ra, int *rb, int *rc)
>
> then the function can be shorter.

Fixed here and elsewhere.

> An empty line is needed between declaration and statement.

Fixed.

>> +  /* We found a whole lot of stack adjustments.  Be safe, tell GDB that
>> +     the epilogue stack unwind is in progress even if we didn't see a
>> +     return yet.  */
>> +  if (ninsns == max_insns)
>> +    return 1;
>
> I am confused by the comments and code.  Infer from your code above that
> GCC emits arbitrary number of sp-adjusting continuous instructions in
> epilogue, is that true?

No, GCC doesn't emit an arbitrary number of SP-adjusting instructions, 
but what should GDB do if it gets some code that looks like that?  I've 
rewritten the comment to make it more clear that this is a "shouldn't 
happen" situation.

> Looks the epilogue detection isn't precise nor accurate.  Supposing we
> have an epilogue like this below,
>
>     1. ADDI sp, sp, n
>     2. RET
>
> if pc points at insn #1, nios2_in_epilogue_p returns true, which isn't
> precise, because the stack frame isn't destroyed at the moment pc points
> at insn #1.  gdbarch in_function_epilogue_p's name is misleading, and
> better to be renamed to stack_frame_destroyed_p (it's on my todo list).
>
> if pc points at insn #2, nios2_in_epilogue_p returns false, which isn't
> accurate.

I think that you have gotten confused by the pc and insn variables being 
updated out of sync in the scanning loop.  I have rewritten the loop 
code and the comments to make it more obvious that scanning for stack 
adjustments starts at the instruction before current_pc.  It doesn't 
matter if there have been additional stack adjustments in the sequence 
of instructions before that -- we only need to see one to know that the 
stack teardown is already in progress at current_pc.

> Why do we change from byte_order_for_code to byte_order?

Leftover bits from some other changes that aren't ready to commit yet. 
I've put it back the way it was.

New patch set coming up shortly.

-Sandra

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

* [1/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-24 23:52   ` Sandra Loosemore
  2014-11-25  0:03     ` [3/3 patch, nios2] clean up prologue/epilogue detection code, v2 Sandra Loosemore
  2014-11-25  0:03     ` [2/3 " Sandra Loosemore
@ 2014-11-25  0:03     ` Sandra Loosemore
  2014-11-25  2:50       ` Yao Qi
  2 siblings, 1 reply; 9+ messages in thread
From: Sandra Loosemore @ 2014-11-25  0:03 UTC (permalink / raw)
  To: Yao Qi, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 209 bytes --]

This part of the revised Nios II prologue/epilogue refactoring patch set 
includes only the parts that introduce new helper functions to match and 
disassemble instructions.  OK to commit, this time?

-Sandra

[-- Attachment #2: gdb-nios2-helpers-1.log --]
[-- Type: text/x-log, Size: 850 bytes --]

2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>

	gdb/
	* nios2-tdep.c (nios2_fetch_insn): Move up in file.  Disassemble
	the instruction as well as reading it from memory.
	(nios2_match_add): New.
	(nios2_match_sub): New.
	(nios2_match_addi): New.
	(nios2_match_orhi): New.
	(nios2_match_stw): New.
	(nios2_match_ldw): New.
	(nios2_match_rdctl): New.
	(enum branch_condition): New.
	(nios2_match_branch): New.
	(nios2_match_jmpi): New.
	(nios2_match_calli): New.
	(nios2_match_jmpr): New.
	(nios2_match_callr): New.
	(nios2_match_break): New.
	(nios2_match_trap): New.
	(nios2_in_epilogue_p): Rewrite to use new functions.
	(nios2_analyze_prologue): Likewise.
	(nios2_skip_prologue): Delete unused local limit_pc.
	(nios2_breakpoint_from_pc): Make R1-specific encodings explicit.
	(nios2_get_next_pc): Rewrite to use new functions.

	

	

	

[-- Attachment #3: gdb-nios2-helpers-1.patch --]
[-- Type: text/x-patch, Size: 32105 bytes --]

diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
index 1b647ac..aa26af9 100644
--- a/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -275,45 +275,360 @@ nios2_init_cache (struct nios2_unwind_cache *cache, CORE_ADDR pc)
   nios2_setup_default (cache);
 }
 
+/* Read and identify an instruction at PC.  If INSNP is non-null,
+   store the instruction word into that location.  Return the opcode
+   pointer or NULL if the memory couldn't be read or disassembled.  */
+
+static const struct nios2_opcode *
+nios2_fetch_insn (struct gdbarch *gdbarch, CORE_ADDR pc,
+		  unsigned int *insnp)
+{
+  LONGEST memword;
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  unsigned int insn;
+
+  if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE,
+				 gdbarch_byte_order (gdbarch), &memword))
+    return NULL;
+
+  insn = (unsigned int) memword;
+  if (insnp)
+    *insnp = insn;
+  return nios2_find_opcode_hash (insn, mach);
+}
+
+
+/* Match and disassemble an ADD-type instruction, with 3 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_add (uint32_t insn, const struct nios2_opcode *op,
+		 unsigned long mach, int *ra, int *rb, int *rc)
+{
+  if (op->match == MATCH_R1_ADD || op->match == MATCH_R1_MOV)
+    {
+      *ra = GET_IW_R_A (insn);
+      *rb = GET_IW_R_B (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a SUB-type instruction, with 3 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_sub (uint32_t insn, const struct nios2_opcode *op,
+		 unsigned long mach, int *ra, int *rb, int *rc)
+{
+  if (op->match == MATCH_R1_SUB)
+    {
+      *ra = GET_IW_R_A (insn);
+      *rb = GET_IW_R_B (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an ADDI-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_addi (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_ADDI)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an ORHI-type instruction, with 2 register operands
+   and one unsigned immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_orhi (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, int *ra, int *rb, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_ORHI)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *uimm = GET_IW_I_IMM16 (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a STW-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_stw (uint32_t insn, const struct nios2_opcode *op,
+		 unsigned long mach, int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_STW || op->match == MATCH_R1_STWIO)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a LDW-type instruction, with 2 register operands
+   and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_ldw (uint32_t insn, const struct nios2_opcode *op,
+		 unsigned long mach, int *ra, int *rb, int *imm)
+{
+  if (op->match == MATCH_R1_LDW || op->match == MATCH_R1_LDWIO)
+    {
+      *ra = GET_IW_I_A (insn);
+      *rb = GET_IW_I_B (insn);
+      *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a RDCTL instruction, with 2 register operands.
+   Returns true on success, and fills in the operand pointers.  */
+
+static int
+nios2_match_rdctl (uint32_t insn, const struct nios2_opcode *op,
+		   unsigned long mach, int *ra, int *rc)
+{
+  if (op->match == MATCH_R1_RDCTL)
+    {
+      *ra = GET_IW_R_IMM5 (insn);
+      *rc = GET_IW_R_C (insn);
+      return 1;
+    }
+  return 0;
+}
+
+
+/* Match and disassemble a branch instruction, with (potentially)
+   2 register operands and one immediate operand.
+   Returns true on success, and fills in the operand pointers.  */
+
+enum branch_condition {
+  branch_none,
+  branch_eq,
+  branch_ne,
+  branch_ge,
+  branch_geu,
+  branch_lt,
+  branch_ltu
+};
+  
+static int
+nios2_match_branch (uint32_t insn, const struct nios2_opcode *op,
+		    unsigned long mach, int *ra, int *rb, int *imm,
+		    enum branch_condition *cond)
+{
+  switch (op->match)
+    {
+    case MATCH_R1_BR:
+      *cond = branch_none;
+      break;
+    case MATCH_R1_BEQ:
+      *cond = branch_eq;
+      break;
+    case MATCH_R1_BNE:
+      *cond = branch_ne;
+      break;
+    case MATCH_R1_BGE:
+      *cond = branch_ge;
+      break;
+    case MATCH_R1_BGEU:
+      *cond = branch_geu;
+      break;
+    case MATCH_R1_BLT:
+      *cond = branch_lt;
+      break;
+    case MATCH_R1_BLTU:
+      *cond = branch_ltu;
+      break;
+    default:
+      return 0;
+    }
+  *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16;
+  *ra = GET_IW_I_A (insn);
+  *rb = GET_IW_I_B (insn);
+  return 1;
+}
+
+/* Match and disassemble a direct jump instruction, with an
+   unsigned operand.  Returns true on success, and fills in the operand
+   pointer.  */
+
+static int
+nios2_match_jmpi (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_JMPI)
+    {
+      *uimm = GET_IW_J_IMM26 (insn) << 2;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a direct call instruction, with an
+   unsigned operand.  Returns true on success, and fills in the operand
+   pointer.  */
+
+static int
+nios2_match_calli (uint32_t insn, const struct nios2_opcode *op,
+		   unsigned long mach, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_CALL)
+    {
+      *uimm = GET_IW_J_IMM26 (insn) << 2;
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble an indirect jump instruction, with a
+   (possibly implicit) register operand.  Returns true on success, and fills
+   in the operand pointer.  */
+
+static int
+nios2_match_jmpr (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, int *ra)
+{
+  switch (op->match)
+    {
+    case MATCH_R1_JMP:
+      *ra = GET_IW_I_A (insn);
+      return 1;
+    case MATCH_R1_RET:
+      *ra = NIOS2_RA_REGNUM;
+      return 1;
+    case MATCH_R1_ERET:
+      *ra = NIOS2_EA_REGNUM;
+      return 1;
+    case MATCH_R1_BRET:
+      *ra = NIOS2_BA_REGNUM;
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Match and disassemble an indirect call instruction, with a register
+   operand.  Returns true on success, and fills in the operand pointer.  */
+
+static int
+nios2_match_callr (uint32_t insn, const struct nios2_opcode *op,
+		   unsigned long mach, int *ra)
+{
+  if (op->match == MATCH_R1_CALLR)
+    {
+      *ra = GET_IW_I_A (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a break instruction, with an unsigned operand.
+   Returns true on success, and fills in the operand pointer.  */
+
+static int
+nios2_match_break (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_BREAK)
+    {
+      *uimm = GET_IW_R_IMM5 (insn);
+      return 1;
+    }
+  return 0;
+}
+
+/* Match and disassemble a trap instruction, with an unsigned operand.
+   Returns true on success, and fills in the operand pointer.  */
+
+static int
+nios2_match_trap (uint32_t insn, const struct nios2_opcode *op,
+		  unsigned long mach, unsigned int *uimm)
+{
+  if (op->match == MATCH_R1_TRAP)
+    {
+      *uimm = GET_IW_R_IMM5 (insn);
+      return 1;
+    }
+  return 0;
+}
+
 /* Helper function to identify when we're in a function epilogue;
    that is, the part of the function from the point at which the
-   stack adjustment is made, to the return or sibcall.  On Nios II,
-   we want to check that the CURRENT_PC is a return-type instruction
-   and that the previous instruction is a stack adjustment.
-   START_PC is the beginning of the function in question.  */
+   stack adjustments are made, to the return or sibcall.
+   Note that we may have several stack adjustment instructions, and
+   this function needs to test whether the stack teardown has already
+   started before current_pc, not whether it has completed.  */
 
 static int
 nios2_in_epilogue_p (struct gdbarch *gdbarch,
 		     CORE_ADDR current_pc,
 		     CORE_ADDR start_pc)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  unsigned int insn;
+  const struct nios2_opcode *op = NULL;
+  unsigned int uimm;
+  int imm;
+  int ra, rb, rc;
+  enum branch_condition cond;
 
   /* There has to be a previous instruction in the function.  */
   if (current_pc > start_pc)
     {
-
-      /* Check whether the previous instruction was a stack
-	 adjustment.  */
-      unsigned int insn
-        = read_memory_unsigned_integer (current_pc - NIOS2_OPCODE_SIZE,
-					NIOS2_OPCODE_SIZE, byte_order);
-
-      if ((insn & 0xffc0003c) == 0xdec00004	/* ADDI sp, sp, */
-	  || (insn & 0xffc1ffff) == 0xdec1883a	/* ADD  sp, sp, */
-	  || (insn & 0xffc0003f) == 0xdec00017)	/* LDW  sp, constant(sp) */
-	{
-	  /* Then check if it's followed by a return or a tail
-	     call.  */
-          insn = read_memory_unsigned_integer (current_pc, NIOS2_OPCODE_SIZE,
-					       byte_order);
-
-	  if (insn == 0xf800283a			/* RET */
-	      || insn == 0xe800083a			/* ERET */
-	      || (insn & 0x07ffffff) == 0x0000683a	/* JMP */
-	      || (insn & 0xffc0003f) == 6)		/* BR */
-	    return 1;
-	}
+      int ok = 0;
+
+      /* Check whether the previous instruction was a stack adjustment.
+	 Possible instructions here include:
+	 ADDI sp, sp, n
+	 ADD sp, sp, rn
+	 LDW sp, n(sp)  */
+      op = nios2_fetch_insn (gdbarch, current_pc - NIOS2_OPCODE_SIZE, &insn);
+      if (op == NULL)
+	return 0;
+
+      /* Was it a stack adjustment?  */
+      if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
+	ok = (rb == NIOS2_SP_REGNUM);
+      else if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
+	ok = (rc == NIOS2_SP_REGNUM);
+      else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm))
+	ok = (rb == NIOS2_SP_REGNUM);
+      if (!ok)
+	return 0;
+
+      /* Then check if it's followed by a return or a tail call.  */
+      op = nios2_fetch_insn (gdbarch, current_pc, &insn);
+      if (op == NULL)
+       return 0;
+      if (nios2_match_jmpr (insn, op, mach, &ra)
+         || nios2_match_jmpi (insn, op, mach, &uimm)
+         || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+             && cond == branch_none))
+       return 1;
     }
   return 0;
 }
@@ -337,31 +652,33 @@ nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
    interested in skipping the prologue.  Otherwise CACHE is filled in
    from the frame information.
 
-   The prologue will consist of the following parts:
-     1) Optional profiling instrumentation.
-        This uses two or three instructions (the last of
-	these might get merged in with the STW which saves RA to the
-	stack).  We interpret these.
+   The prologue may consist of the following parts:
+     1) Profiling instrumentation.  For non-PIC code it looks like:
 	  mov	 r8, ra
 	  call	 mcount
 	  mov	 ra, r8
 
-     2) A stack adjustment or stack which, which will be one of:
-	  addi   sp, sp, -constant
-	or:
-	  movi   r8, constant
-	  sub    sp, sp, r8
-	or
-	  movhi  r8, constant
-	  addi   r8, r8, constant
-	  sub    sp, sp, r8
-	or
+     2) A stack adjustment and save of R4-R7 for varargs functions.
+        This is typically merged with item 3.
+
+     3) A stack adjustment and save of the callee-saved registers;
+	typically an explicit SP decrement and individual register
+	saves.
+
+        There may also be a stack switch here in an exception handler
+	in place of a stack adjustment.  It looks like:
 	  movhi  rx, %hiadj(newstack)
 	  addhi  rx, rx, %lo(newstack)
 	  stw    sp, constant(rx)
 	  mov    sp, rx
 
-     3) An optional stack check, which can take either of these forms:
+     5) A frame pointer save, which can be either a MOV or ADDI.
+
+     6) A further stack pointer adjustment.  This is normally included
+        adjustment in step 4 unless the total adjustment is too large
+	to be done in one step.
+
+     7) A stack overflow check, which can take either of these forms:
 	  bgeu   sp, rx, +8
 	  break  3
 	or
@@ -369,32 +686,18 @@ nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
 	  ...
 	.Lstack_overflow:
 	  break  3
+        If present, this is inserted after the stack pointer adjustments
+	for steps 3, 4, and 6.
 
-     4) Saving any registers which need to be saved.  These will
-        normally just be stored onto the stack:
-	  stw    rx, constant(sp)
-	but in the large frame case will use r8 as an offset back
-	to the cfa:
-	  add    r8, r8, sp
-	  stw    rx, -constant(r8)
-
-	Saving control registers looks slightly different:
-	  rdctl  rx, ctlN
-	  stw    rx, constant(sp)
-
-     5) An optional FP setup, either if the user has requested a
-        frame pointer or if the function calls alloca.
-        This is always:
-	  mov    fp, sp
-
-    The prologue instructions may be interleaved, and the register
-    saves and FP setup can occur in either order.
+    The prologue instructions may be combined or interleaved with other
+    instructions.
 
     To cope with all this variability we decode all the instructions
-    from the start of the prologue until we hit a branch, call or
-    return.  For each of the instructions mentioned in 3, 4 and 5 we
-    handle the limited cases of stores to the stack and operations
-    on constant values.  */
+    from the start of the prologue until we hit an instruction that
+    cannot possibly be a prologue instruction, such as a branch, call,
+    return, or epilogue instruction.  The prologue is considered to end
+    at the last instruction that can definitely be considered a
+    prologue instruction.  */
 
 static CORE_ADDR
 nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
@@ -402,12 +705,13 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 			struct nios2_unwind_cache *cache,
 			struct frame_info *this_frame)
 {
-  /* Maximum lines of prologue to check.
+  /* Maximum number of possibly-prologue instructions to check.
      Note that this number should not be too large, else we can
      potentially end up iterating through unmapped memory.  */
-  CORE_ADDR limit_pc = start_pc + 200;
+  int ninsns, max_insns = 50;
   int regno;
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
 
   /* Does the frame set up the FP register?  */
   int base_reg = 0;
@@ -428,9 +732,7 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
      functions which switch stacks?  */
   CORE_ADDR frame_high;
 
-  /* Is this the end of the prologue?  */
-  int within_prologue = 1;
-
+  /* The last definitely-prologue instruction seen.  */
   CORE_ADDR prologue_end;
 
   /* Is this the innermost function?  */
@@ -444,15 +746,19 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 
   /* Set up the default values of the registers.  */
   nios2_setup_default (cache);
-  prologue_end = start_pc;
 
   /* Find the prologue instructions.  */
-  while (pc < limit_pc && within_prologue)
+  prologue_end = start_pc;
+  for (ninsns = 0; ninsns < max_insns; ninsns++)
     {
       /* Present instruction.  */
       uint32_t insn;
-
-      int prologue_insn = 0;
+      const struct nios2_opcode *op;
+      int ra, rb, rc, imm;
+      unsigned int uimm;
+      unsigned int reglist;
+      int wb, ret;
+      enum branch_condition cond;
 
       if (pc == current_pc)
       {
@@ -466,22 +772,21 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	  fprintf_unfiltered (gdb_stdlog, "*");
       }
 
-      insn = read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-      pc += NIOS2_OPCODE_SIZE;
+      op = nios2_fetch_insn (gdbarch, pc, &insn);
+
+      /* Unknown opcode?  Stop scanning.  */
+      if (op == NULL)
+	break;
+      pc += op->size;
 
       if (nios2_debug)
 	fprintf_unfiltered (gdb_stdlog, "[%08X]", insn);
 
       /* The following instructions can appear in the prologue.  */
 
-      if ((insn & MASK_R1_ADD) == MATCH_R1_ADD)
+      if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
 	{
 	  /* ADD   rc, ra, rb  (also used for MOV) */
-
-	  int ra = GET_IW_R_A (insn);
-	  int rb = GET_IW_R_B (insn);
-	  int rc = GET_IW_R_C (insn);
-
 	  if (rc == NIOS2_SP_REGNUM
 	      && rb == 0
 	      && value[ra].reg == cache->reg_saved[NIOS2_SP_REGNUM].basereg)
@@ -522,17 +827,13 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 		value[rc].reg = -1;
 	      value[rc].offset = value[ra].offset + value[rb].offset;
 	    }
-	  prologue_insn = 1;
-	}
 
-      else if ((insn & MASK_R1_SUB) == MATCH_R1_SUB)
+	  prologue_end = pc;
+	}
+      
+      else if (nios2_match_sub (insn, op, mach, &ra, &rb, &rc))
 	{
 	  /* SUB   rc, ra, rb */
-
-	  int ra = GET_IW_R_A (insn);
-	  int rb = GET_IW_R_B (insn);
-	  int rc = GET_IW_R_C (insn);
-
 	  if (rc != 0)
 	    {
 	      if (value[rb].reg == 0)
@@ -543,12 +844,9 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	    }
 	}
 
-      else if ((insn & MASK_R1_ADDI) == MATCH_R1_ADDI)
+      else if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
 	{
-	  /* ADDI  rb, ra, immed   (also used for MOVI) */
-	  short immed = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
+	  /* ADDI    rb, ra, imm */
 
 	  /* The first stack adjustment is part of the prologue.
 	     Any subsequent stack adjustments are either down to
@@ -561,166 +859,136 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 	  if (rb != 0)
 	    {
 	      value[rb].reg    = value[ra].reg;
-	      value[rb].offset = value[ra].offset + immed;
+	      value[rb].offset = value[ra].offset + imm;
 	    }
 
-	  prologue_insn = 1;
+	  prologue_end = pc;
 	}
 
-      else if ((insn & MASK_R1_ORHI) == MATCH_R1_ORHI)
+      else if (nios2_match_orhi (insn, op, mach, &ra, &rb, &uimm))
 	{
-	  /* ORHI  rb, ra, immed   (also used for MOVHI) */
-	  unsigned int immed = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
-
+	  /* ORHI  rb, ra, uimm   (also used for MOVHI) */
 	  if (rb != 0)
 	    {
   	      value[rb].reg    = (value[ra].reg == 0) ? 0 : -1;
-	      value[rb].offset = value[ra].offset | (immed << 16);
+	      value[rb].offset = value[ra].offset | (uimm << 16);
 	    }
 	}
 
-      else if ((insn & MASK_R1_STW) == MATCH_R1_STW
-	       || (insn & MASK_R1_STWIO) == MATCH_R1_STWIO)
+      else if (nios2_match_stw (insn, op, mach, &ra, &rb, &imm))
         {
-	  /* STW rb, immediate(ra) */
+	  /* STW rb, imm(ra) */
 
-	  short immed16 = GET_IW_I_IMM16 (insn);
-	  int ra = GET_IW_I_A (insn);
-	  int rb = GET_IW_I_B (insn);
-
-	  /* Are we storing the original value of a register?
+	  /* Are we storing the original value of a register to the stack?
 	     For exception handlers the value of EA-4 (return
 	     address from interrupts etc) is sometimes stored.  */
 	  int orig = value[rb].reg;
 	  if (orig > 0
 	      && (value[rb].offset == 0
-		  || (orig == NIOS2_EA_REGNUM && value[rb].offset == -4)))
-	    {
-	      /* We are most interested in stores to the stack, but
-		 also take note of stores to other places as they
-		 might be useful later.  */
-	      if ((value[ra].reg == NIOS2_SP_REGNUM
+		  || (orig == NIOS2_EA_REGNUM && value[rb].offset == -4))
+	      && ((value[ra].reg == NIOS2_SP_REGNUM
 		   && cache->reg_saved[orig].basereg != NIOS2_SP_REGNUM)
-		  || cache->reg_saved[orig].basereg == -1)
+		  || cache->reg_saved[orig].basereg == -1))
+	    {
+	      if (pc < current_pc)
 		{
-		  if (pc < current_pc)
-		    {
-		      /* Save off callee saved registers.  */
-		      cache->reg_saved[orig].basereg = value[ra].reg;
-		      cache->reg_saved[orig].addr = value[ra].offset + immed16;
-		    }
-
-		  prologue_insn = 1;
-
-		  if (orig == NIOS2_EA_REGNUM || orig == NIOS2_ESTATUS_REGNUM)
-		    exception_handler = 1;
+		  /* Save off callee saved registers.  */
+		  cache->reg_saved[orig].basereg = value[ra].reg;
+		  cache->reg_saved[orig].addr = value[ra].offset + imm;
 		}
+	      
+	      prologue_end = pc;
+	      
+	      if (orig == NIOS2_EA_REGNUM || orig == NIOS2_ESTATUS_REGNUM)
+		exception_handler = 1;
 	    }
 	  else
-	    /* Non-stack memory writes are not part of the
-	       prologue.  */
-	    within_prologue = 0;
+	    /* Non-stack memory writes cannot appear in the prologue.  */
+	    break;
         }
 
-      else if ((insn & MASK_R1_RDCTL) == MATCH_R1_RDCTL)
+      else if (nios2_match_rdctl (insn, op, mach, &ra, &rc))
 	{
-	  /* RDCTL rC, ctlN */
-	  int rc = GET_IW_R_C (insn);
-	  int n = GET_IW_R_A (insn);
-
+	  /* RDCTL rC, ctlN
+	     This can appear in exception handlers in combination with
+	     a subsequent save to the stack frame.  */
 	  if (rc != 0)
 	    {
-	      value[rc].reg    = NIOS2_STATUS_REGNUM + n;
+	      value[rc].reg    = NIOS2_STATUS_REGNUM + ra;
 	      value[rc].offset = 0;
 	    }
-
-	  prologue_insn = 1;
         }
 
-      else if ((insn & MASK_R1_CALL) == MATCH_R1_CALL
-	       && value[8].reg == NIOS2_RA_REGNUM
-	       && value[8].offset == 0
-	       && value[NIOS2_SP_REGNUM].reg == NIOS2_SP_REGNUM
-	       && value[NIOS2_SP_REGNUM].offset == 0)
+      else if (nios2_match_calli (insn, op, mach, &uimm))
 	{
-	  /* A CALL instruction.  This is treated as a call to mcount
-	     if ra has been stored into r8 beforehand and if it's
-	     before the stack adjust.
-	     Note mcount corrupts r2-r3, r9-r15 & ra.  */
-	  for (i = 2 ; i <= 3 ; i++)
-	    value[i].reg = -1;
-	  for (i = 9 ; i <= 15 ; i++)
-	    value[i].reg = -1;
-	  value[NIOS2_RA_REGNUM].reg = -1;
-
-	  prologue_insn = 1;
-	}
+	  if (value[8].reg == NIOS2_RA_REGNUM
+	      && value[8].offset == 0
+	      && value[NIOS2_SP_REGNUM].reg == NIOS2_SP_REGNUM
+	      && value[NIOS2_SP_REGNUM].offset == 0)
+	    {
+	      /* A CALL instruction.  This is treated as a call to mcount
+		 if ra has been stored into r8 beforehand and if it's
+		 before the stack adjust.
+		 Note mcount corrupts r2-r3, r9-r15 & ra.  */
+	      for (i = 2 ; i <= 3 ; i++)
+		value[i].reg = -1;
+	      for (i = 9 ; i <= 15 ; i++)
+		value[i].reg = -1;
+	      value[NIOS2_RA_REGNUM].reg = -1;
+
+	      prologue_end = pc;
+	    }
 
-      else if ((insn & 0xf83fffff) == 0xd800012e)
-	{
-	   /* BGEU sp, rx, +8
-	      BREAK 3
-	      This instruction sequence is used in stack checking;
-	      we can ignore it.  */
-	  unsigned int next_insn
-	    = read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-
-	  if (next_insn != 0x003da0fa)
-	    within_prologue = 0;
+	  /* Other calls are not part of the prologue.  */
 	  else
-	    pc += NIOS2_OPCODE_SIZE;
-	}
-
-      else if ((insn & 0xf800003f) == 0xd8000036)
-	{
-	   /* BLTU sp, rx, .Lstackoverflow
-	      If the location branched to holds a BREAK 3 instruction
-	      then this is also stack overflow detection.  We can
-	      ignore it.  */
-	  CORE_ADDR target_pc = pc + ((insn & 0x3fffc0) >> 6);
-	  unsigned int target_insn
-	    = read_memory_unsigned_integer (target_pc, NIOS2_OPCODE_SIZE,
-					    byte_order);
-
-	  if (target_insn != 0x003da0fa)
-	    within_prologue = 0;
+	    break;
 	}
 
-      /* Any other instructions are allowed to be moved up into the
-	 prologue.  If we reach a branch, call or return then the
-	 prologue is considered over.  We also consider a second stack
-	 adjustment as terminating the prologue (see above).  */
-      else
+      else if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond))
 	{
-	  switch (GET_IW_R1_OP (insn))
+	  /* Branches not involving a stack overflow check aren't part of
+	     the prologue.  */
+	  if (ra != NIOS2_SP_REGNUM)
+	    break;
+	  else if (cond == branch_geu)
 	    {
-	    case R1_OP_BEQ:
-	    case R1_OP_BGE:
-	    case R1_OP_BGEU:
-	    case R1_OP_BLT:
-	    case R1_OP_BLTU:
-	    case R1_OP_BNE:
-	    case R1_OP_BR:
-	    case R1_OP_CALL:
-	      within_prologue = 0;
-	      break;
-	    case R1_OP_OPX:
-	      if (GET_IW_R_OPX (insn) == R1_OPX_RET
-		  || GET_IW_R_OPX (insn) == R1_OPX_ERET
-		  || GET_IW_R_OPX (insn) == R1_OPX_BRET
-		  || GET_IW_R_OPX (insn) == R1_OPX_CALLR
-		  || GET_IW_R_OPX (insn) == R1_OPX_JMP)
-		within_prologue = 0;
-	      break;
-	    default:
-	      break;
+	      /* BGEU sp, rx, +8
+		 BREAK 3
+		 This instruction sequence is used in stack checking;
+		 we can ignore it.  */
+	      unsigned int next_insn;
+	      const struct nios2_opcode *next_op
+		= nios2_fetch_insn (gdbarch, pc, &next_insn);
+	      if (next_op != NULL
+		  && nios2_match_break (next_insn, op, mach, &uimm))
+		pc += next_op->size;
+	      else
+		break;
 	    }
+	  else if (cond == branch_ltu)
+	    {
+	      /* BLTU sp, rx, .Lstackoverflow
+		 If the location branched to holds a BREAK 3 instruction
+		 then this is also stack overflow detection.  */
+	      unsigned int next_insn;
+	      const struct nios2_opcode *next_op
+		= nios2_fetch_insn (gdbarch, pc + imm, &next_insn);
+	      if (next_op != NULL
+		  && nios2_match_break (next_insn, op, mach, &uimm))
+		;
+	      else
+		break;
+	    }
+	  else
+	    break;
 	}
 
-      if (prologue_insn)
-	prologue_end = pc;
+      /* All other calls or jumps (including returns) terminate 
+	 the prologue.  */
+      else if (nios2_match_callr (insn, op, mach, &ra)
+	       || nios2_match_jmpr (insn, op, mach, &ra)
+	       || nios2_match_jmpi (insn, op, mach, &uimm))
+	break;
     }
 
   /* If THIS_FRAME is NULL, we are being called from skip_prologue
@@ -858,7 +1126,6 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc,
 static CORE_ADDR
 nios2_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
 {
-  CORE_ADDR limit_pc;
   CORE_ADDR func_addr;
 
   struct nios2_unwind_cache cache;
@@ -886,21 +1153,19 @@ static const gdb_byte*
 nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
 			  int *bp_size)
 {
-  /* break encoding: 31->27  26->22  21->17  16->11 10->6 5->0 */
-  /*                 00000   00000   0x1d    0x2d   11111 0x3a */
-  /*                 00000   00000   11101   101101 11111 111010 */
-  /* In bytes:       00000000 00111011 01101111 11111010 */
-  /*                 0x0       0x3b    0x6f     0xfa */
-  static const gdb_byte breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0};
-  static const gdb_byte breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa};
-
   enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
-
-  *bp_size = 4;
-  if (gdbarch_byte_order_for_code (gdbarch) == BFD_ENDIAN_BIG)
-    return breakpoint_be;
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+
+  /* R1 break encoding:
+     ((0x1e << 17) | (0x34 << 11) | (0x1f << 6) | (0x3a << 0))
+     0x003da7fa */
+  static const gdb_byte r1_breakpoint_le[] = {0xfa, 0xa7, 0x3d, 0x0};
+  static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3d, 0xa7, 0xfa};
+  *bp_size = NIOS2_OPCODE_SIZE;
+  if (byte_order_for_code == BFD_ENDIAN_BIG)
+    return r1_breakpoint_be;
   else
-    return breakpoint_le;
+    return r1_breakpoint_le;
 }
 
 /* Implement the print_insn gdbarch method.  */
@@ -1256,15 +1521,7 @@ static const struct frame_unwind nios2_stub_frame_unwind =
   nios2_stub_frame_sniffer
 };
 
-/* Helper function to read an instruction at PC.  */
 
-static unsigned long
-nios2_fetch_instruction (struct gdbarch *gdbarch, CORE_ADDR pc)
-{
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-
-  return read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
-}
 
 /* Determine where to set a single step breakpoint while considering
    branch prediction.  */
@@ -1274,88 +1531,79 @@ nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  unsigned long inst;
-  int op;
-  int imm16;
+  unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  unsigned int insn;
+  const struct nios2_opcode *op = nios2_fetch_insn (gdbarch, pc, &insn);
   int ra;
   int rb;
-  int ras;
-  int rbs;
-  unsigned int rau;
-  unsigned int rbu;
-
-  inst = nios2_fetch_instruction (gdbarch, pc);
-  pc += NIOS2_OPCODE_SIZE;
-
-  imm16 = (short) GET_IW_I_IMM16 (inst);
-  ra = GET_IW_I_A (inst);
-  rb = GET_IW_I_B (inst);
-  ras = get_frame_register_signed (frame, ra);
-  rbs = get_frame_register_signed (frame, rb);
-  rau = get_frame_register_unsigned (frame, ra);
-  rbu = get_frame_register_unsigned (frame, rb);
-
-  switch (GET_IW_R1_OP (inst))
+  int imm;
+  unsigned int uimm;
+  int wb, ret;
+  enum branch_condition cond;
+
+  /* Do something stupid if we can't disassemble the insn at pc.  */
+  if (op == NULL)
+    return pc + NIOS2_OPCODE_SIZE;
+    
+  if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond))
     {
-    case R1_OP_BEQ:
-      if (ras == rbs)
-	pc += imm16;
-      break;
-
-    case R1_OP_BGE:
-      if (ras >= rbs)
-        pc += imm16;
-      break;
-
-    case R1_OP_BGEU:
-      if (rau >= rbu)
-        pc += imm16;
-      break;
-
-    case R1_OP_BLT:
-      if (ras < rbs)
-        pc += imm16;
-      break;
+      int ras = get_frame_register_signed (frame, ra);
+      int rbs = get_frame_register_signed (frame, rb);
+      unsigned int rau = get_frame_register_unsigned (frame, ra);
+      unsigned int rbu = get_frame_register_unsigned (frame, rb);
 
-    case R1_OP_BLTU:
-      if (rau < rbu)
-        pc += imm16;
-      break;
-
-    case R1_OP_BNE:
-      if (ras != rbs)
-        pc += imm16;
-      break;
-
-    case R1_OP_BR:
-      pc += imm16;
-      break;
-
-    case R1_OP_JMPI:
-    case R1_OP_CALL:
-      pc = (pc & 0xf0000000) | (GET_IW_J_IMM26 (inst) << 2);
-      break;
-
-    case R1_OP_OPX:
-      switch (GET_IW_R_OPX (inst))
+      pc += op->size;
+      switch (cond)
 	{
-	case R1_OPX_JMP:
-	case R1_OPX_CALLR:
-	case R1_OPX_RET:
-	  pc = ras;
+	case branch_none:
+	  pc += imm;
+	  break;
+	case branch_eq:
+	  if (ras == rbs)
+	    pc += imm;
+	  break;
+	case branch_ne:
+	  if (ras != rbs)
+	    pc += imm;
+	  break;
+	case branch_ge:
+	  if (ras >= rbs)
+	    pc += imm;
+	  break;
+	case branch_geu:
+	  if (rau >= rbu)
+	    pc += imm;
+	  break;
+	case branch_lt:
+	  if (ras < rbs)
+	    pc += imm;
+	  break;
+	case branch_ltu:
+	  if (rau < rbu)
+	    pc += imm;
 	  break;
-
-	case R1_OPX_TRAP:
-	  if (tdep->syscall_next_pc != NULL)
-	    return tdep->syscall_next_pc (frame);
-
 	default:
 	  break;
 	}
-      break;
-    default:
-      break;
     }
+
+  else if (nios2_match_jmpi (insn, op, mach, &uimm)
+	   || nios2_match_calli (insn, op, mach, &uimm))
+    pc = (pc & 0xf0000000) | uimm;
+
+  else if (nios2_match_jmpr (insn, op, mach, &ra)
+	   || nios2_match_callr (insn, op, mach, &ra))
+    pc = get_frame_register_unsigned (frame, ra);
+
+  else if (nios2_match_trap (insn, op, mach, &uimm))
+    {
+      if (tdep->syscall_next_pc != NULL)
+	return tdep->syscall_next_pc (frame);
+    }
+
+  else
+    pc += op->size;
+
   return pc;
 }
 

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

* [3/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-24 23:52   ` Sandra Loosemore
@ 2014-11-25  0:03     ` Sandra Loosemore
  2014-11-25  3:00       ` Yao Qi
  2014-11-25  0:03     ` [2/3 " Sandra Loosemore
  2014-11-25  0:03     ` [1/3 " Sandra Loosemore
  2 siblings, 1 reply; 9+ messages in thread
From: Sandra Loosemore @ 2014-11-25  0:03 UTC (permalink / raw)
  To: Yao Qi, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 420 bytes --]

This part of the revised Nios II prologue/epilogue refactoring patch 
fixes the prologue analyzer to handle the multiple stack adjustment 
instructions that GCC may now emit in some cases.  It replaces the test 
that caused analysis to terminate when a second adjustment is found with 
more specific tests that can distinguish between prologue-ish and 
epilogue-ish adjustments.

OK to commit on top of part 1?

-Sandra

[-- Attachment #2: gdb-nios2-prologue-3.log --]
[-- Type: text/x-log, Size: 247 bytes --]

2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>

	gdb/
	* nios2-tdep.c (nios2_analyze_prologue): Replace restriction
	that there can be only one stack adjustment in the prologue
	with tests to detect specific disallowed stack adjustments.

[-- Attachment #3: gdb-nios2-prologue-3.patch --]
[-- Type: text/x-patch, Size: 2148 bytes --]

diff -u b/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
--- b/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -842,6 +842,11 @@
 	      cache->reg_saved[NIOS2_SP_REGNUM].addr = -4;
 	    }
 
+	  else if (rc == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
+	    /* This is setting SP from FP.  This only happens in the
+	       function epilogue.  */
+	    break;
+
 	  else if (rc != 0)
 	    {
 	      if (value[rb].reg == 0)
@@ -853,13 +858,21 @@
 	      value[rc].offset = value[ra].offset + value[rb].offset;
 	    }
 
-	  prologue_end = pc;
+	  /* The add/move is only considered a prologue instruction
+	     if the destination is SP or FP.  */
+	  if (rc == NIOS2_SP_REGNUM || rc == NIOS2_FP_REGNUM)
+	    prologue_end = pc;
 	}
       
       else if (nios2_match_sub (insn, op, mach, &ra, &rb, &rc))
 	{
 	  /* SUB   rc, ra, rb */
-	  if (rc != 0)
+	  if (rc == NIOS2_SP_REGNUM && rb == NIOS2_SP_REGNUM
+	      && value[rc].reg != 0)
+	    /* If we are decrementing the SP by a non-constant amount,
+	       this is alloca, not part of the prologue.  */
+	    break;
+	  else if (rc != 0)
 	    {
 	      if (value[rb].reg == 0)
 		value[rc].reg = value[ra].reg;
@@ -873,12 +886,13 @@
 	{
 	  /* ADDI    rb, ra, imm */
 
-	  /* The first stack adjustment is part of the prologue.
-	     Any subsequent stack adjustments are either down to
-	     alloca or the epilogue so stop analysing when we hit
-	     them.  */
+	  /* A positive stack adjustment has to be part of the epilogue.  */
 	  if (rb == NIOS2_SP_REGNUM
-	      && (value[rb].offset != 0 || value[ra].reg != NIOS2_SP_REGNUM))
+	      && (imm > 0 || value[ra].reg != NIOS2_SP_REGNUM))
+	    break;
+
+	  /* Likewise restoring SP from FP.  */
+	  else if (rb == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
 	    break;
 
 	  if (rb != 0)
@@ -887,7 +901,10 @@
 	      value[rb].offset = value[ra].offset + imm;
 	    }
 
-	  prologue_end = pc;
+	  /* The add is only considered a prologue instruction
+	     if the destination is SP or FP.  */
+	  if (rb == NIOS2_SP_REGNUM || rb == NIOS2_FP_REGNUM)
+	    prologue_end = pc;
 	}
 
       else if (nios2_match_orhi (insn, op, mach, &ra, &rb, &uimm))

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

* [2/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-24 23:52   ` Sandra Loosemore
  2014-11-25  0:03     ` [3/3 patch, nios2] clean up prologue/epilogue detection code, v2 Sandra Loosemore
@ 2014-11-25  0:03     ` Sandra Loosemore
  2014-11-25  3:29       ` Yao Qi
  2014-11-25  0:03     ` [1/3 " Sandra Loosemore
  2 siblings, 1 reply; 9+ messages in thread
From: Sandra Loosemore @ 2014-11-25  0:03 UTC (permalink / raw)
  To: Yao Qi, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 233 bytes --]

This part of the revised Nios II prologue/epilogue refactoring patch set 
fixes the epilogue detection code to deal with the multiple stack 
adjustments that GCC may now emit in some cases.  OK to commit on top of 
part 1?

-Sandra


[-- Attachment #2: gdb-nios2-epilogue-2.log --]
[-- Type: text/x-log, Size: 138 bytes --]

2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>

	gdb/
	* nios2-tdep.c (nios2_in_epilogue_p): Handle multiple stack
	adjustments.

[-- Attachment #3: gdb-nios2-epilogue-2.patch --]
[-- Type: text/x-patch, Size: 2943 bytes --]

diff -u b/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
--- b/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -589,26 +589,42 @@
 		     CORE_ADDR start_pc)
 {
   unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+  /* Maximum number of possibly-epilogue instructions to check.
+     Note that this number should not be too large, else we can
+     potentially end up iterating through unmapped memory.  */
+  int ninsns, max_insns = 5;
   unsigned int insn;
   const struct nios2_opcode *op = NULL;
   unsigned int uimm;
   int imm;
   int ra, rb, rc;
   enum branch_condition cond;
+  CORE_ADDR pc;
 
   /* There has to be a previous instruction in the function.  */
-  if (current_pc > start_pc)
-    {
-      int ok = 0;
+  if (current_pc <= start_pc)
+    return 0;
 
-      /* Check whether the previous instruction was a stack adjustment.
-	 Possible instructions here include:
+  /* Find the previous instruction before current_pc.
+     For the moment we will assume that all instructions are the
+     same size here.  */
+  pc = current_pc - NIOS2_OPCODE_SIZE;
+
+  /* Beginning with the previous instruction we just located, check whether
+     we are in a sequence of at least one stack adjustment instruction.
+     Possible instructions here include:
 	 ADDI sp, sp, n
 	 ADD sp, sp, rn
 	 LDW sp, n(sp)  */
-      op = nios2_fetch_insn (gdbarch, current_pc - NIOS2_OPCODE_SIZE, &insn);
+  for (ninsns = 0; ninsns < max_insns; ninsns++)
+    {
+      int ok = 0;
+
+      /* Fetch the insn at pc.  */
+      op = nios2_fetch_insn (gdbarch, pc, &insn);
       if (op == NULL)
 	return 0;
+      pc += op->size;
 
       /* Was it a stack adjustment?  */
       if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
@@ -618,18 +634,27 @@
       else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm))
 	ok = (rb == NIOS2_SP_REGNUM);
       if (!ok)
-	return 0;
-
-      /* Then check if it's followed by a return or a tail call.  */
-      op = nios2_fetch_insn (gdbarch, current_pc, &insn);
-      if (op == NULL)
-       return 0;
-      if (nios2_match_jmpr (insn, op, mach, &ra)
-         || nios2_match_jmpi (insn, op, mach, &uimm)
-         || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
-             && cond == branch_none))
-       return 1;
+	break;
     }
+
+  /* No stack adjustments found.  */
+  if (ninsns == 0)
+    return 0;
+
+  /* We found more stack adjustments than we expect GCC to be generating.
+     Since it looks like a stack unwind might be in progress tell GDB to
+     treat it as such.  */
+  if (ninsns == max_insns)
+    return 1;
+
+  /* The next instruction following the stack adjustments must be a
+     return, jump, or unconditional branch.  */
+  if (nios2_match_jmpr (insn, op, mach, &ra)
+      || nios2_match_jmpi (insn, op, mach, &uimm)
+      || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+	  && cond == branch_none))
+    return 1;
+
   return 0;
 }
 

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

* Re: [1/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-25  0:03     ` [1/3 " Sandra Loosemore
@ 2014-11-25  2:50       ` Yao Qi
  0 siblings, 0 replies; 9+ messages in thread
From: Yao Qi @ 2014-11-25  2:50 UTC (permalink / raw)
  To: Sandra Loosemore; +Cc: gdb-patches

Sandra Loosemore <sandra@codesourcery.com> writes:

> This part of the revised Nios II prologue/epilogue refactoring patch
> set includes only the parts that introduce new helper functions to
> match and disassemble instructions.  OK to commit, this time?
>
> -Sandra
>
> 2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>
>
> 	gdb/
> 	* nios2-tdep.c (nios2_fetch_insn): Move up in file.  Disassemble
> 	the instruction as well as reading it from memory.
> 	(nios2_match_add): New.
> 	(nios2_match_sub): New.
> 	(nios2_match_addi): New.
> 	(nios2_match_orhi): New.
> 	(nios2_match_stw): New.
> 	(nios2_match_ldw): New.
> 	(nios2_match_rdctl): New.
> 	(enum branch_condition): New.
> 	(nios2_match_branch): New.
> 	(nios2_match_jmpi): New.
> 	(nios2_match_calli): New.
> 	(nios2_match_jmpr): New.
> 	(nios2_match_callr): New.
> 	(nios2_match_break): New.
> 	(nios2_match_trap): New.
> 	(nios2_in_epilogue_p): Rewrite to use new functions.
> 	(nios2_analyze_prologue): Likewise.
> 	(nios2_skip_prologue): Delete unused local limit_pc.
> 	(nios2_breakpoint_from_pc): Make R1-specific encodings explicit.
> 	(nios2_get_next_pc): Rewrite to use new functions.

This is OK.

-- 
Yao (齐尧)

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

* Re: [3/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-25  0:03     ` [3/3 patch, nios2] clean up prologue/epilogue detection code, v2 Sandra Loosemore
@ 2014-11-25  3:00       ` Yao Qi
  0 siblings, 0 replies; 9+ messages in thread
From: Yao Qi @ 2014-11-25  3:00 UTC (permalink / raw)
  To: Sandra Loosemore; +Cc: gdb-patches

Sandra Loosemore <sandra@codesourcery.com> writes:

> This part of the revised Nios II prologue/epilogue refactoring patch
> fixes the prologue analyzer to handle the multiple stack adjustment
> instructions that GCC may now emit in some cases.  It replaces the
> test that caused analysis to terminate when a second adjustment is
> found with more specific tests that can distinguish between
> prologue-ish and epilogue-ish adjustments.
>
> OK to commit on top of part 1?
>
> -Sandra
>
> 2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>
>
> 	gdb/
> 	* nios2-tdep.c (nios2_analyze_prologue): Replace restriction
> 	that there can be only one stack adjustment in the prologue
> 	with tests to detect specific disallowed stack adjustments.

This is OK.

-- 
Yao (齐尧)

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

* Re: [2/3 patch, nios2] clean up prologue/epilogue detection code, v2
  2014-11-25  0:03     ` [2/3 " Sandra Loosemore
@ 2014-11-25  3:29       ` Yao Qi
  0 siblings, 0 replies; 9+ messages in thread
From: Yao Qi @ 2014-11-25  3:29 UTC (permalink / raw)
  To: Sandra Loosemore; +Cc: gdb-patches

Sandra Loosemore <sandra@codesourcery.com> writes:

> This part of the revised Nios II prologue/epilogue refactoring patch
> set fixes the epilogue detection code to deal with the multiple stack
> adjustments that GCC may now emit in some cases.  OK to commit on top
> of part 1?
>
> -Sandra
>
>
> 2014-11-24  Sandra Loosemore  <sandra@codesourcery.com>
>
> 	gdb/
> 	* nios2-tdep.c (nios2_in_epilogue_p): Handle multiple stack
> 	adjustments.

This is OK.

-- 
Yao (齐尧)

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

end of thread, other threads:[~2014-11-25  3:29 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-08 23:01 [patch, nios2] clean up prologue/epilogue detection code Sandra Loosemore
2014-11-11  3:33 ` Yao Qi
2014-11-24 23:52   ` Sandra Loosemore
2014-11-25  0:03     ` [3/3 patch, nios2] clean up prologue/epilogue detection code, v2 Sandra Loosemore
2014-11-25  3:00       ` Yao Qi
2014-11-25  0:03     ` [2/3 " Sandra Loosemore
2014-11-25  3:29       ` Yao Qi
2014-11-25  0:03     ` [1/3 " Sandra Loosemore
2014-11-25  2:50       ` Yao Qi

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).