/* Target-machine dependent code for Nios II, for GDB.
- Copyright (C) 2012-2013 Free Software Foundation, Inc.
+ Copyright (C) 2012-2017 Free Software Foundation, Inc.
Contributed by Peter Brookes (pbrookes@altera.com)
and Andrew Draper (adraper@altera.com).
Contributed by Mentor Graphics, Inc.
#include "value.h"
#include "symfile.h"
#include "arch-utils.h"
-#include "floatformat.h"
-#include "gdb_assert.h"
#include "infcall.h"
#include "regset.h"
#include "target-descriptions.h"
/* To get entry_point_address. */
#include "objfiles.h"
-
-/* Nios II ISA specific encodings and macros. */
-#include "opcode/nios2.h"
+#include <algorithm>
/* Nios II specific header. */
#include "nios2-tdep.h"
NIOS2_MPUACC_REGNUM /* 48 */
};
+gdb_static_assert (ARRAY_SIZE (nios2_dwarf2gdb_regno_map) == NIOS2_NUM_REGS);
/* Implement the dwarf2_reg_to_regnum gdbarch method. */
static int
nios2_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int dw_reg)
{
- if (dw_reg < 0 || dw_reg > NIOS2_NUM_REGS)
- {
- warning (_("Dwarf-2 uses unmapped register #%d"), dw_reg);
- return dw_reg;
- }
+ if (dw_reg < 0 || dw_reg >= NIOS2_NUM_REGS)
+ return -1;
return nios2_dwarf2gdb_regno_map[dw_reg];
}
nios2_setup_default (cache);
}
-/* 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. */
+/* 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 (mach == bfd_mach_nios2r2)
+ {
+ if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE,
+ BFD_ENDIAN_LITTLE, &memword)
+ && !safe_read_memory_integer (pc, NIOS2_CDX_OPCODE_SIZE,
+ BFD_ENDIAN_LITTLE, &memword))
+ return NULL;
+ }
+ else 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_in_epilogue_p (struct gdbarch *gdbarch,
- CORE_ADDR current_pc,
- CORE_ADDR start_pc)
+nios2_match_add (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, int *ra, int *rb, int *rc)
{
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int is_r2 = (mach == bfd_mach_nios2r2);
- /* There has to be a previous instruction in the function. */
- if (current_pc > start_pc)
+ if (!is_r2 && (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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_ADD || op->match == MATCH_R2_MOV)
+ {
+ *ra = GET_IW_F3X6L5_A (insn);
+ *rb = GET_IW_F3X6L5_B (insn);
+ *rc = GET_IW_F3X6L5_C (insn);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_ADD_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T3X1_A3 (insn)];
+ *rb = nios2_r2_reg3_mappings[GET_IW_T3X1_B3 (insn)];
+ *rc = nios2_r2_reg3_mappings[GET_IW_T3X1_C3 (insn)];
+ return 1;
+ }
+ else if (op->match == MATCH_R2_MOV_N)
+ {
+ *ra = GET_IW_F2_A (insn);
+ *rb = 0;
+ *rc = GET_IW_F2_B (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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && 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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_SUB)
+ {
+ *ra = GET_IW_F3X6L5_A (insn);
+ *rb = GET_IW_F3X6L5_B (insn);
+ *rc = GET_IW_F3X6L5_C (insn);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_SUB_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T3X1_A3 (insn)];
+ *rb = nios2_r2_reg3_mappings[GET_IW_T3X1_B3 (insn)];
+ *rc = nios2_r2_reg3_mappings[GET_IW_T3X1_C3 (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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && 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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_ADDI)
+ {
+ *ra = GET_IW_F2I16_A (insn);
+ *rb = GET_IW_F2I16_B (insn);
+ *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_ADDI_N || op->match == MATCH_R2_SUBI_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T2X1I3_A3 (insn)];
+ *rb = nios2_r2_reg3_mappings[GET_IW_T2X1I3_B3 (insn)];
+ *imm = nios2_r2_asi_n_mappings[GET_IW_T2X1I3_IMM3 (insn)];
+ if (op->match == MATCH_R2_SUBI_N)
+ *imm = - (*imm);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_SPADDI_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)];
+ *rb = NIOS2_SP_REGNUM;
+ *imm = GET_IW_T1I7_IMM7 (insn) << 2;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_SPINCI_N || op->match == MATCH_R2_SPDECI_N)
+ {
+ *ra = NIOS2_SP_REGNUM;
+ *rb = NIOS2_SP_REGNUM;
+ *imm = GET_IW_X1I7_IMM7 (insn) << 2;
+ if (op->match == MATCH_R2_SPDECI_N)
+ *imm = - (*imm);
+ 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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && 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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_ORHI)
+ {
+ *ra = GET_IW_F2I16_A (insn);
+ *rb = GET_IW_F2I16_B (insn);
+ *uimm = GET_IW_F2I16_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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && (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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_STW)
+ {
+ *ra = GET_IW_F2I16_A (insn);
+ *rb = GET_IW_F2I16_B (insn);
+ *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_STWIO)
+ {
+ *ra = GET_IW_F2X4I12_A (insn);
+ *rb = GET_IW_F2X4I12_B (insn);
+ *imm = (signed) (GET_IW_F2X4I12_IMM12 (insn) << 20) >> 20;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_STW_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T2I4_A3 (insn)];
+ *rb = nios2_r2_reg3_mappings[GET_IW_T2I4_B3 (insn)];
+ *imm = GET_IW_T2I4_IMM4 (insn) << 2;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_STWSP_N)
+ {
+ *ra = NIOS2_SP_REGNUM;
+ *rb = GET_IW_F1I5_B (insn);
+ *imm = GET_IW_F1I5_IMM5 (insn) << 2;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_STWZ_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T1X1I6_A3 (insn)];
+ *rb = 0;
+ *imm = GET_IW_T1X1I6_IMM6 (insn) << 2;
+ 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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && (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;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_LDW)
+ {
+ *ra = GET_IW_F2I16_A (insn);
+ *rb = GET_IW_F2I16_B (insn);
+ *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_LDWIO)
{
+ *ra = GET_IW_F2X4I12_A (insn);
+ *rb = GET_IW_F2X4I12_B (insn);
+ *imm = (signed) (GET_IW_F2X4I12_IMM12 (insn) << 20) >> 20;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_LDW_N)
+ {
+ *ra = nios2_r2_reg3_mappings[GET_IW_T2I4_A3 (insn)];
+ *rb = nios2_r2_reg3_mappings[GET_IW_T2I4_B3 (insn)];
+ *imm = GET_IW_T2I4_IMM4 (insn) << 2;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_LDWSP_N)
+ {
+ *ra = NIOS2_SP_REGNUM;
+ *rb = GET_IW_F1I5_B (insn);
+ *imm = GET_IW_F1I5_IMM5 (insn) << 2;
+ return 1;
+ }
+ 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);
+/* Match and disassemble a RDCTL instruction, with 2 register operands.
+ Returns true on success, and fills in the operand pointers. */
- if ((insn & 0xffc0003c) == 0xdec00004 /* ADDI sp, sp, */
- || (insn & 0xffc1ffff) == 0xdec1883a /* ADD sp, sp, */
- || (insn & 0xffc0003f) == 0xdec00017) /* LDW sp, constant(sp) */
+static int
+nios2_match_rdctl (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, int *ra, int *rc)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && (op->match == MATCH_R1_RDCTL))
+ {
+ *ra = GET_IW_R_IMM5 (insn);
+ *rc = GET_IW_R_C (insn);
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_RDCTL)
+ {
+ *ra = GET_IW_F3X6L5_IMM5 (insn);
+ *rc = GET_IW_F3X6L5_C (insn);
+ return 1;
+ }
+ return 0;
+}
+
+/* Match and disassemble a PUSH.N or STWM instruction.
+ Returns true on success, and fills in the operand pointers. */
+
+static int
+nios2_match_stwm (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, unsigned int *reglist,
+ int *ra, int *imm, int *wb, int *id)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_PUSH_N)
+ {
+ *reglist = 1 << 31;
+ if (GET_IW_L5I4X1_FP (insn))
+ *reglist |= (1 << 28);
+ if (GET_IW_L5I4X1_CS (insn))
{
- /* 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 val = GET_IW_L5I4X1_REGRANGE (insn);
+ *reglist |= nios2_r2_reg_range_mappings[val];
}
+ *ra = NIOS2_SP_REGNUM;
+ *imm = GET_IW_L5I4X1_IMM4 (insn) << 2;
+ *wb = 1;
+ *id = 0;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_STWM)
+ {
+ unsigned int rawmask = GET_IW_F1X4L17_REGMASK (insn);
+ if (GET_IW_F1X4L17_RS (insn))
+ {
+ *reglist = ((rawmask << 14) & 0x00ffc000);
+ if (rawmask & (1 << 10))
+ *reglist |= (1 << 28);
+ if (rawmask & (1 << 11))
+ *reglist |= (1 << 31);
+ }
+ else
+ *reglist = rawmask << 2;
+ *ra = GET_IW_F1X4L17_A (insn);
+ *imm = 0;
+ *wb = GET_IW_F1X4L17_WB (insn);
+ *id = GET_IW_F1X4L17_ID (insn);
+ return 1;
}
return 0;
}
-/* Implement the in_function_epilogue_p gdbarch method. */
+/* Match and disassemble a POP.N or LDWM instruction.
+ Returns true on success, and fills in the operand pointers. */
static int
-nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+nios2_match_ldwm (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, unsigned int *reglist,
+ int *ra, int *imm, int *wb, int *id, int *ret)
{
- CORE_ADDR func_addr;
+ int is_r2 = (mach == bfd_mach_nios2r2);
- if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
- return nios2_in_epilogue_p (gdbarch, pc, func_addr);
+ if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_POP_N)
+ {
+ *reglist = 1 << 31;
+ if (GET_IW_L5I4X1_FP (insn))
+ *reglist |= (1 << 28);
+ if (GET_IW_L5I4X1_CS (insn))
+ {
+ int val = GET_IW_L5I4X1_REGRANGE (insn);
+ *reglist |= nios2_r2_reg_range_mappings[val];
+ }
+ *ra = NIOS2_SP_REGNUM;
+ *imm = GET_IW_L5I4X1_IMM4 (insn) << 2;
+ *wb = 1;
+ *id = 1;
+ *ret = 1;
+ return 1;
+ }
+ else if (op->match == MATCH_R2_LDWM)
+ {
+ unsigned int rawmask = GET_IW_F1X4L17_REGMASK (insn);
+ if (GET_IW_F1X4L17_RS (insn))
+ {
+ *reglist = ((rawmask << 14) & 0x00ffc000);
+ if (rawmask & (1 << 10))
+ *reglist |= (1 << 28);
+ if (rawmask & (1 << 11))
+ *reglist |= (1 << 31);
+ }
+ else
+ *reglist = rawmask << 2;
+ *ra = GET_IW_F1X4L17_A (insn);
+ *imm = 0;
+ *wb = GET_IW_F1X4L17_WB (insn);
+ *id = GET_IW_F1X4L17_ID (insn);
+ *ret = GET_IW_F1X4L17_PC (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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2)
+ {
+ 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;
+ }
+ else
+ {
+ switch (op->match)
+ {
+ case MATCH_R2_BR_N:
+ *cond = branch_none;
+ *ra = NIOS2_Z_REGNUM;
+ *rb = NIOS2_Z_REGNUM;
+ *imm = (signed) ((GET_IW_I10_IMM10 (insn) << 1) << 21) >> 21;
+ return 1;
+ case MATCH_R2_BEQZ_N:
+ *cond = branch_eq;
+ *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)];
+ *rb = NIOS2_Z_REGNUM;
+ *imm = (signed) ((GET_IW_T1I7_IMM7 (insn) << 1) << 24) >> 24;
+ return 1;
+ case MATCH_R2_BNEZ_N:
+ *cond = branch_ne;
+ *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)];
+ *rb = NIOS2_Z_REGNUM;
+ *imm = (signed) ((GET_IW_T1I7_IMM7 (insn) << 1) << 24) >> 24;
+ return 1;
+ case MATCH_R2_BR:
+ *cond = branch_none;
+ break;
+ case MATCH_R2_BEQ:
+ *cond = branch_eq;
+ break;
+ case MATCH_R2_BNE:
+ *cond = branch_ne;
+ break;
+ case MATCH_R2_BGE:
+ *cond = branch_ge;
+ break;
+ case MATCH_R2_BGEU:
+ *cond = branch_geu;
+ break;
+ case MATCH_R2_BLT:
+ *cond = branch_lt;
+ break;
+ case MATCH_R2_BLTU:
+ *cond = branch_ltu;
+ break;
+ default:
+ return 0;
+ }
+ *ra = GET_IW_F2I16_A (insn);
+ *rb = GET_IW_F2I16_B (insn);
+ *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16;
+ return 1;
+ }
return 0;
}
-/* Define some instruction patterns supporting wildcard bits via a
- mask. */
+/* Match and disassemble a direct jump instruction, with an
+ unsigned operand. Returns true on success, and fills in the operand
+ pointer. */
-typedef struct
+static int
+nios2_match_jmpi (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, unsigned int *uimm)
{
- unsigned int insn;
- unsigned int mask;
-} wild_insn;
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && op->match == MATCH_R1_JMPI)
+ {
+ *uimm = GET_IW_J_IMM26 (insn) << 2;
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_JMPI)
+ {
+ *uimm = GET_IW_L26_IMM26 (insn) << 2;
+ return 1;
+ }
+ return 0;
+}
-static const wild_insn profiler_insn[] =
+/* 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)
{
- { 0x0010e03a, 0x00000000 }, /* nextpc r8 */
- { 0xf813883a, 0x00000000 }, /* mov r9,ra */
- { 0x02800034, 0x003fffc0 }, /* movhi r10,257 */
- { 0x52800004, 0x003fffc0 }, /* addi r10,r10,-31992 */
- { 0x00000000, 0xffffffc0 }, /* call <mcount> */
- { 0x483f883a, 0x00000000 } /* mov ra,r9 */
-};
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && op->match == MATCH_R1_CALL)
+ {
+ *uimm = GET_IW_J_IMM26 (insn) << 2;
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_CALL)
+ {
+ *uimm = GET_IW_L26_IMM26 (insn) << 2;
+ return 1;
+ }
+ return 0;
+}
-static const wild_insn irqentry_insn[] =
+/* 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)
{
- { 0x0031307a, 0x00000000 }, /* rdctl et,estatus */
- { 0xc600004c, 0x00000000 }, /* andi et,et,1 */
- { 0xc0000026, 0x003fffc0 }, /* beq et,zero, <software_exception> */
- { 0x0031313a, 0x00000000 }, /* rdctl et,ipending */
- { 0xc0000026, 0x003fffc0 } /* beq et,zero, <software_exception> */
-};
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2)
+ 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;
+ }
+ else
+ switch (op->match)
+ {
+ case MATCH_R2_JMP:
+ *ra = GET_IW_F2I16_A (insn);
+ return 1;
+ case MATCH_R2_JMPR_N:
+ *ra = GET_IW_F1X1_A (insn);
+ return 1;
+ case MATCH_R2_RET:
+ case MATCH_R2_RET_N:
+ *ra = NIOS2_RA_REGNUM;
+ return 1;
+ case MATCH_R2_ERET:
+ *ra = NIOS2_EA_REGNUM;
+ return 1;
+ case MATCH_R2_BRET:
+ *ra = NIOS2_BA_REGNUM;
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+/* Match and disassemble an indirect call instruction, with a register
+ operand. Returns true on success, and fills in the operand pointer. */
-/* Attempt to match SEQUENCE, which is COUNT insns long, at START_PC. */
+static int
+nios2_match_callr (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, int *ra)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && op->match == MATCH_R1_CALLR)
+ {
+ *ra = GET_IW_I_A (insn);
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_CALLR)
+ {
+ *ra = GET_IW_F2I16_A (insn);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_CALLR_N)
+ {
+ *ra = GET_IW_F1X1_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_sequence (struct gdbarch *gdbarch, CORE_ADDR start_pc,
- const wild_insn *sequence, int count)
+nios2_match_break (uint32_t insn, const struct nios2_opcode *op,
+ unsigned long mach, unsigned int *uimm)
{
- CORE_ADDR pc = start_pc;
- int i;
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && op->match == MATCH_R1_BREAK)
+ {
+ *uimm = GET_IW_R_IMM5 (insn);
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_BREAK)
+ {
+ *uimm = GET_IW_F3X6L5_IMM5 (insn);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_BREAK_N)
+ {
+ *uimm = GET_IW_X2L5_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)
+{
+ int is_r2 = (mach == bfd_mach_nios2r2);
+
+ if (!is_r2 && op->match == MATCH_R1_TRAP)
+ {
+ *uimm = GET_IW_R_IMM5 (insn);
+ return 1;
+ }
+ else if (!is_r2)
+ return 0;
+ else if (op->match == MATCH_R2_TRAP)
+ {
+ *uimm = GET_IW_F3X6L5_IMM5 (insn);
+ return 1;
+ }
+ else if (op->match == MATCH_R2_TRAP_N)
+ {
+ *uimm = GET_IW_X2L5_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 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)
+{
+ unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ int is_r2 = (mach == bfd_mach_nios2r2);
+ /* 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;
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ const struct nios2_opcode *op = NULL;
+ unsigned int uimm;
+ int imm;
+ int wb, id, ret;
+ 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)
+ return 0;
- for (i = 0 ; i < count ; i++)
+ /* Find the previous instruction before current_pc. For R2, it might
+ be either a 16-bit or 32-bit instruction; the only way to know for
+ sure is to scan through from the beginning of the function,
+ disassembling as we go. */
+ if (is_r2)
+ for (pc = start_pc; ; )
+ {
+ op = nios2_fetch_insn (gdbarch, pc, &insn);
+ if (op == NULL)
+ return 0;
+ if (pc + op->size < current_pc)
+ pc += op->size;
+ else
+ break;
+ /* We can skip over insns to a forward branch target. Since
+ the branch offset is relative to the next instruction,
+ it's correct to do this after incrementing the pc above. */
+ if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+ && imm > 0
+ && pc + imm < current_pc)
+ pc += imm;
+ }
+ /* Otherwise just go back to the previous 32-bit insn. */
+ else
+ 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)
+ SPINCI.N n
+ LDWSP.N sp, n(sp)
+ LDWM {reglist}, (sp)++, wb */
+ for (ninsns = 0; ninsns < max_insns; ninsns++)
{
- insn = read_memory_unsigned_integer (pc, NIOS2_OPCODE_SIZE, byte_order);
- if ((insn & ~sequence[i].mask) != sequence[i].insn)
- return 0;
+ int ok = 0;
- pc += NIOS2_OPCODE_SIZE;
+ /* 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))
+ 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);
+ else if (nios2_match_ldwm (insn, op, mach, &uimm, &ra,
+ &imm, &wb, &ret, &id))
+ ok = (ra == NIOS2_SP_REGNUM && wb && id);
+ if (!ok)
+ break;
}
- return 1;
+ /* 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, or a CDX pop.n or ldwm
+ that does an implicit return. */
+ if (nios2_match_jmpr (insn, op, mach, &ra)
+ || nios2_match_jmpi (insn, op, mach, &uimm)
+ || (nios2_match_ldwm (insn, op, mach, &uimm, &ra, &imm, &wb, &id, &ret)
+ && ret)
+ || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+ && cond == branch_none))
+ return 1;
+
+ return 0;
+}
+
+/* Implement the stack_frame_destroyed_p gdbarch method. */
+
+static int
+nios2_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ CORE_ADDR func_addr;
+
+ if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
+ return nios2_in_epilogue_p (gdbarch, pc, func_addr);
+
+ return 0;
}
/* Do prologue analysis, returning the PC of the first instruction
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. The old version uses six
- instructions. We step over this if there is an exact match.
- nextpc r8
- mov r9, ra
- movhi r10, %hiadj(.LP2)
- addi r10, r10, %lo(.LP2)
- call mcount
- mov ra, r9
- The new version 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) Optional interrupt entry decision. Again, we step over
- this if there is an exact match.
- rdctl et,estatus
- andi et,et,1
- beq et,zero, <software_exception>
- rdctl et,ipending
- beq et,zero, <software_exception>
-
- 3) 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.
+ For R2 CDX this is typically handled with a STWM, otherwise
+ this is typically merged with item 3.
+
+ 3) A stack adjustment and save of the callee-saved registers.
+ For R2 CDX these are typically handled with a PUSH.N or STWM,
+ otherwise as 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
- 4) An optional stack check, which can take either of these forms:
+ 4) A frame pointer save, which can be either a MOV or ADDI.
+
+ 5) A further stack pointer adjustment. This is normally included
+ adjustment in step 3 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
+ trap 3
or
bltu sp, rx, .Lstack_overflow
...
.Lstack_overflow:
- break 3
+ trap 3
+
+ Older versions of GCC emitted "break 3" instead of "trap 3" here,
+ so we check for both cases.
- 5) 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)
+ Older GCC versions emitted stack overflow checks after the SP
+ adjustments in both steps 3 and 4. Starting with GCC 6, there is
+ at most one overflow check, which is placed before the first
+ stack adjustment for R2 CDX and after the first stack adjustment
+ otherwise.
- Saving control registers looks slightly different:
- rdctl rx, ctlN
- stw rx, constant(sp)
-
- 6) 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,
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 regno;
+ int ninsns, max_insns = 50;
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;
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? */
/* Set up the default values of the registers. */
nios2_setup_default (cache);
- /* If the first few instructions are the profile entry, then skip
- over them. Newer versions of the compiler use more efficient
- profiling code. */
- if (nios2_match_sequence (gdbarch, pc, profiler_insn,
- ARRAY_SIZE (profiler_insn)))
- pc += ARRAY_SIZE (profiler_insn) * NIOS2_OPCODE_SIZE;
-
- /* If the first few instructions are an interrupt entry, then skip
- over them too. */
- if (nios2_match_sequence (gdbarch, pc, irqentry_insn,
- ARRAY_SIZE (irqentry_insn)))
- {
- pc += ARRAY_SIZE (irqentry_insn) * NIOS2_OPCODE_SIZE;
- exception_handler = 1;
- }
-
- 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, id, ret;
+ enum branch_condition cond;
if (pc == current_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);
+ {
+ if (op->size == 2)
+ fprintf_unfiltered (gdb_stdlog, "[%04X]", insn & 0xffff);
+ else
+ fprintf_unfiltered (gdb_stdlog, "[%08X]", insn);
+ }
/* The following instructions can appear in the prologue. */
- if ((insn & 0x0001ffff) == 0x0001883a)
+ if (nios2_match_add (insn, op, mach, &ra, &rb, &rc))
{
/* ADD rc, ra, rb (also used for MOV) */
-
- int ra = GET_IW_A (insn);
- int rb = GET_IW_B (insn);
- int rc = GET_IW_C (insn);
-
if (rc == NIOS2_SP_REGNUM
&& rb == 0
&& value[ra].reg == cache->reg_saved[NIOS2_SP_REGNUM].basereg)
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)
value[rc].reg = -1;
value[rc].offset = value[ra].offset + value[rb].offset;
}
- prologue_insn = 1;
- }
- else if ((insn & 0x0001ffff) == 0x0001983a)
+ /* 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_A (insn);
- int rb = GET_IW_B (insn);
- int rc = GET_IW_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;
}
}
- else if ((insn & 0x0000003f) == 0x00000004)
+ else if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
{
- /* ADDI rb, ra, immed (also used for MOVI) */
- short immed = GET_IW_IMM16 (insn);
- int ra = GET_IW_A (insn);
- int rb = GET_IW_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 & 0x0000003f) == 0x00000034)
+ else if (nios2_match_orhi (insn, op, mach, &ra, &rb, &uimm))
{
- /* ORHI rb, ra, immed (also used for MOVHI) */
- unsigned int immed = GET_IW_IMM16 (insn);
- int ra = GET_IW_A (insn);
- int rb = GET_IW_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 & IW_OP_MASK) == OP_STW
- || (insn & IW_OP_MASK) == OP_STWIO)
+ else if (nios2_match_stw (insn, op, mach, &ra, &rb, &imm))
{
- /* STW rb, immediate(ra) */
+ /* STW rb, imm(ra) */
- short immed16 = GET_IW_IMM16 (insn);
- int ra = GET_IW_A (insn);
- int rb = GET_IW_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)))
+ || (orig == NIOS2_EA_REGNUM && value[rb].offset == -4))
+ && value[ra].reg == NIOS2_SP_REGNUM)
{
- /* 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
- && cache->reg_saved[orig].basereg != NIOS2_SP_REGNUM)
- || 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 + GET_IW_IMM16 (insn);
- }
-
- 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 & 0xffc1f83f) == 0x0001303a)
+ else if (nios2_match_stwm (insn, op, mach,
+ ®list, &ra, &imm, &wb, &id))
{
- /* RDCTL rC, ctlN */
- int rc = GET_IW_C (insn);
- int n = GET_IW_CONTROL_REGNUM (insn);
+ /* PUSH.N {reglist}, adjust
+ or
+ STWM {reglist}, --(SP)[, writeback] */
+ int i;
+ int off = 0;
+
+ if (ra != NIOS2_SP_REGNUM || id != 0)
+ /* This is a non-stack-push memory write and cannot be
+ part of the prologue. */
+ break;
+ for (i = 31; i >= 0; i--)
+ if (reglist & (1 << i))
+ {
+ int orig = value[i].reg;
+
+ off += 4;
+ if (orig > 0 && value[i].offset == 0 && pc < current_pc)
+ {
+ cache->reg_saved[orig].basereg
+ = value[NIOS2_SP_REGNUM].reg;
+ cache->reg_saved[orig].addr
+ = value[NIOS2_SP_REGNUM].offset - off;
+ }
+ }
+
+ if (wb)
+ value[NIOS2_SP_REGNUM].offset -= off;
+ value[NIOS2_SP_REGNUM].offset -= imm;
+
+ prologue_end = pc;
+ }
+
+ else if (nios2_match_rdctl (insn, op, mach, &ra, &rc))
+ {
+ /* 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 & 0x0000003f) == 0
- && 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_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 OP_BEQ:
- case OP_BGE:
- case OP_BGEU:
- case OP_BLT:
- case OP_BLTU:
- case OP_BNE:
- case OP_BR:
- case OP_CALL:
- within_prologue = 0;
- break;
- case OP_OPX:
- if (GET_IW_OPX (insn) == OPX_RET
- || GET_IW_OPX (insn) == OPX_ERET
- || GET_IW_OPX (insn) == OPX_BRET
- || GET_IW_OPX (insn) == OPX_CALLR
- || GET_IW_OPX (insn) == OPX_JMP)
- within_prologue = 0;
- break;
- default:
- break;
+ /* BGEU sp, rx, +8
+ TRAP 3 (or 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_trap (next_insn, op, mach, &uimm)
+ || 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 TRAP or BREAK
+ 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_trap (next_insn, op, mach, &uimm)
+ || nios2_match_break (next_insn, op, mach, &uimm)))
+ ;
+ else
+ break;
+ }
+ else
+ break;
}
- if (prologue_insn)
- prologue_end = pc;
+ /* All other calls, jumps, returns, TRAPs, or BREAKs 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)
+ || (nios2_match_ldwm (insn, op, mach, ®list, &ra,
+ &imm, &wb, &id, &ret)
+ && ret)
+ || nios2_match_trap (insn, op, mach, &uimm)
+ || nios2_match_break (insn, op, mach, &uimm))
+ break;
}
/* If THIS_FRAME is NULL, we are being called from skip_prologue
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;
= skip_prologue_using_sal (gdbarch, func_addr);
if (post_prologue_pc != 0)
- return max (start_pc, post_prologue_pc);
+ return std::max (start_pc, post_prologue_pc);
}
/* Prologue analysis does the rest.... */
return nios2_analyze_prologue (gdbarch, start_pc, start_pc, &cache, NULL);
}
-/* Implement the breakpoint_from_pc gdbarch hook. */
+/* Implement the breakpoint_kind_from_pc gdbarch method. */
-static const gdb_byte*
-nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
- int *bp_size)
+static int
+nios2_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
{
- /* 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;
+
+ if (mach == bfd_mach_nios2r2)
+ {
+ unsigned int insn;
+ const struct nios2_opcode *op
+ = nios2_fetch_insn (gdbarch, *pcptr, &insn);
+
+ if (op && op->size == NIOS2_CDX_OPCODE_SIZE)
+ return NIOS2_CDX_OPCODE_SIZE;
+ else
+ return NIOS2_OPCODE_SIZE;
+ }
else
- return breakpoint_le;
+ return NIOS2_OPCODE_SIZE;
}
-/* Implement the print_insn gdbarch method. */
+/* Implement the sw_breakpoint_from_kind gdbarch method. */
-static int
-nios2_print_insn (bfd_vma memaddr, disassemble_info *info)
+static const gdb_byte *
+nios2_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
{
- if (info->endian == BFD_ENDIAN_BIG)
- return print_insn_big_nios2 (memaddr, info);
+/* The Nios II ABI for Linux says: "Userspace programs should not use
+ the break instruction and userspace debuggers should not insert
+ one." and "Userspace breakpoints are accomplished using the trap
+ instruction with immediate operand 31 (all ones)."
+
+ So, we use "trap 31" consistently as the breakpoint on bare-metal
+ as well as Linux targets. */
+
+ /* R2 trap encoding:
+ ((0x2d << 26) | (0x1f << 21) | (0x1d << 16) | (0x20 << 0))
+ 0xb7fd0020
+ CDX trap.n encoding:
+ ((0xd << 12) | (0x1f << 6) | (0x9 << 0))
+ 0xd7c9
+ Note that code is always little-endian on R2. */
+ *size = kind;
+
+ if (kind == NIOS2_CDX_OPCODE_SIZE)
+ {
+ static const gdb_byte cdx_breakpoint_le[] = {0xc9, 0xd7};
+
+ return cdx_breakpoint_le;
+ }
else
- return print_insn_little_nios2 (memaddr, info);
-}
+ {
+ unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ if (mach == bfd_mach_nios2r2)
+ {
+ static const gdb_byte r2_breakpoint_le[] = {0x20, 0x00, 0xfd, 0xb7};
+
+ return r2_breakpoint_le;
+ }
+ else
+ {
+ enum bfd_endian byte_order_for_code
+ = gdbarch_byte_order_for_code (gdbarch);
+ /* R1 trap encoding:
+ ((0x1d << 17) | (0x2d << 11) | (0x1f << 6) | (0x3a << 0))
+ 0x003b6ffa */
+ static const gdb_byte r1_breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0};
+ static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa};
+
+ if (byte_order_for_code == BFD_ENDIAN_BIG)
+ return r1_breakpoint_be;
+ else
+ return r1_breakpoint_le;
+ }
+ }
+}
/* Implement the frame_align gdbarch method. */
int struct_return, CORE_ADDR struct_addr)
{
int argreg;
- int float_argreg;
int argnum;
int len = 0;
int stack_offset = 0;
for (argnum = 0; argnum < nargs; argnum++)
{
const gdb_byte *val;
- gdb_byte valbuf[MAX_REGISTER_SIZE];
struct value *arg = args[argnum];
struct type *arg_type = check_typedef (value_type (arg));
int len = TYPE_LENGTH (arg_type);
- enum type_code typecode = TYPE_CODE (arg_type);
val = value_contents (arg);
struct gdbarch *gdbarch = get_frame_arch (this_frame);
CORE_ADDR current_pc;
struct nios2_unwind_cache *cache;
- int i;
if (*this_prologue_cache)
- return *this_prologue_cache;
+ return (struct nios2_unwind_cache *) *this_prologue_cache;
cache = FRAME_OBSTACK_ZALLOC (struct nios2_unwind_cache);
*this_prologue_cache = cache;
CORE_ADDR stack_addr;
struct trad_frame_cache *this_trad_cache;
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- int num_regs = gdbarch_num_regs (gdbarch);
if (*this_cache != NULL)
- return *this_cache;
+ return (struct trad_frame_cache *) *this_cache;
this_trad_cache = trad_frame_cache_zalloc (this_frame);
*this_cache = this_trad_cache;
struct frame_info *this_frame, void **cache)
{
gdb_byte dummy[4];
- struct obj_section *s;
CORE_ADDR pc = get_frame_address_in_block (this_frame);
/* Use the stub unwinder for unreadable code. */
return 0;
}
-/* Implement the this_base, this_locals, and this_args hooks
- for the stub unwinder. */
-
-static CORE_ADDR
-nios2_stub_frame_base_address (struct frame_info *this_frame, void **this_cache)
-{
- struct trad_frame_cache *this_trad_cache
- = nios2_stub_frame_cache (this_frame, this_cache);
-
- return trad_frame_get_this_base (this_trad_cache);
-}
-
/* Define the data structures for the stub unwinder. */
static const struct frame_unwind nios2_stub_frame_unwind =
nios2_stub_frame_sniffer
};
-static const struct frame_base nios2_stub_frame_base =
-{
- &nios2_stub_frame_unwind,
- nios2_stub_frame_base_address,
- nios2_stub_frame_base_address,
- nios2_stub_frame_base_address
-};
-/* 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. */
static CORE_ADDR
-nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+nios2_get_next_pc (struct regcache *regcache, CORE_ADDR pc)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch *gdbarch = regcache->arch ();
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_IMM16 (inst);
- ra = GET_IW_A (inst);
- rb = GET_IW_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_OP (inst))
+ int imm;
+ unsigned int uimm;
+ int wb, id, 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 OP_BEQ:
- if (ras == rbs)
- pc += imm16;
- break;
-
- case OP_BGE:
- if (ras >= rbs)
- pc += imm16;
- break;
-
- case OP_BGEU:
- if (rau >= rbu)
- pc += imm16;
- break;
-
- case OP_BLT:
- if (ras < rbs)
- pc += imm16;
- break;
-
- case OP_BLTU:
- if (rau < rbu)
- pc += imm16;
- break;
-
- case OP_BNE:
- if (ras != rbs)
- pc += imm16;
- break;
-
- case OP_BR:
- pc += imm16;
- break;
-
- case OP_JMPI:
- case OP_CALL:
- pc = (pc & 0xf0000000) | (GET_IW_IMM26 (inst) << 2);
- break;
-
- case OP_OPX:
- switch (GET_IW_OPX (inst))
+ int ras = regcache_raw_get_signed (regcache, ra);
+ int rbs = regcache_raw_get_signed (regcache, rb);
+ unsigned int rau = regcache_raw_get_unsigned (regcache, ra);
+ unsigned int rbu = regcache_raw_get_unsigned (regcache, rb);
+
+ pc += op->size;
+ switch (cond)
{
- case OPX_JMP:
- case OPX_CALLR:
- case 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 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 = regcache_raw_get_unsigned (regcache, ra);
+
+ else if (nios2_match_ldwm (insn, op, mach, &uimm, &ra, &imm, &wb, &id, &ret)
+ && ret)
+ {
+ /* If ra is in the reglist, we have to use the value saved in the
+ stack frame rather than the current value. */
+ if (uimm & (1 << NIOS2_RA_REGNUM))
+ pc = nios2_unwind_pc (gdbarch, get_current_frame ());
+ else
+ pc = regcache_raw_get_unsigned (regcache, NIOS2_RA_REGNUM);
+ }
+
+ else if (nios2_match_trap (insn, op, mach, &uimm) && uimm == 0)
+ {
+ if (tdep->syscall_next_pc != NULL)
+ return tdep->syscall_next_pc (get_current_frame (), op);
+ }
+
+ else
+ pc += op->size;
+
return pc;
}
/* Implement the software_single_step gdbarch method. */
-static int
-nios2_software_single_step (struct frame_info *frame)
+static std::vector<CORE_ADDR>
+nios2_software_single_step (struct regcache *regcache)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc = nios2_get_next_pc (frame, get_frame_pc (frame));
-
- insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+ CORE_ADDR next_pc = nios2_get_next_pc (regcache, regcache_read_pc (regcache));
- return 1;
+ return {next_pc};
}
/* Implement the get_longjump_target gdbarch method. */
{
struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
- int register_bytes, i;
+ int i;
struct tdesc_arch_data *tdesc_data = NULL;
const struct target_desc *tdesc = info.target_desc;
/* None found, create a new architecture from the information
provided. */
- tdep = xcalloc (1, sizeof (struct gdbarch_tdep));
+ tdep = XCNEW (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
/* longjmp support not enabled by default. */
set_gdbarch_return_value (gdbarch, nios2_return_value);
set_gdbarch_skip_prologue (gdbarch, nios2_skip_prologue);
- set_gdbarch_in_function_epilogue_p (gdbarch, nios2_in_function_epilogue_p);
- set_gdbarch_breakpoint_from_pc (gdbarch, nios2_breakpoint_from_pc);
+ set_gdbarch_stack_frame_destroyed_p (gdbarch, nios2_stack_frame_destroyed_p);
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch, nios2_breakpoint_kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch, nios2_sw_breakpoint_from_kind);
set_gdbarch_dummy_id (gdbarch, nios2_dummy_id);
set_gdbarch_unwind_pc (gdbarch, nios2_unwind_pc);
frame_base_set_default (gdbarch, &nios2_frame_base);
- set_gdbarch_print_insn (gdbarch, nios2_print_insn);
-
/* Enable inferior call support. */
set_gdbarch_push_dummy_call (gdbarch, nios2_push_dummy_call);
return gdbarch;
}
-extern initialize_file_ftype _initialize_nios2_tdep; /* -Wmissing-prototypes */
-
void
_initialize_nios2_tdep (void)
{