[patch, nios2] clean up prologue/epilogue detection code

Sandra Loosemore
2014-11-08T23:01:34Z

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