diff mbox

[1/3,nios2] clean up prologue/epilogue detection code, v2

Message ID 5473C72B.4000801@codesourcery.com
State New
Headers show

Commit Message

Sandra Loosemore Nov. 25, 2014, 12:02 a.m. UTC
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

Comments

Yao Qi Nov. 25, 2014, 2:50 a.m. UTC | #1
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.
diff mbox

Patch

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;
 }