+ done:
+ xtensa_insnbuf_free(isa, slot);
+ xtensa_insnbuf_free(isa, ins);
+ return found_ret;
+}
+
+/* Call0 opcode class. Opcodes are preclassified according to what they
+ mean for Call0 prologue analysis, and their number of significant operands.
+ The purpose of this is to simplify prologue analysis by separating
+ instruction decoding (libisa) from the semantics of prologue analysis. */
+
+typedef enum
+{
+ c0opc_illegal, /* Unknown to libisa (invalid) or 'ill' opcode. */
+ c0opc_uninteresting, /* Not interesting for Call0 prologue analysis. */
+ c0opc_flow, /* Flow control insn. */
+ c0opc_entry, /* ENTRY indicates non-Call0 prologue. */
+ c0opc_break, /* Debugger software breakpoints. */
+ c0opc_add, /* Adding two registers. */
+ c0opc_addi, /* Adding a register and an immediate. */
+ c0opc_and, /* Bitwise "and"-ing two registers. */
+ c0opc_sub, /* Subtracting a register from a register. */
+ c0opc_mov, /* Moving a register to a register. */
+ c0opc_movi, /* Moving an immediate to a register. */
+ c0opc_l32r, /* Loading a literal. */
+ c0opc_s32i, /* Storing word at fixed offset from a base register. */
+ c0opc_rwxsr, /* RSR, WRS, or XSR instructions. */
+ c0opc_l32e, /* L32E instruction. */
+ c0opc_s32e, /* S32E instruction. */
+ c0opc_rfwo, /* RFWO instruction. */
+ c0opc_rfwu, /* RFWU instruction. */
+ c0opc_NrOf /* Number of opcode classifications. */
+} xtensa_insn_kind;
+
+/* Return true, if OPCNAME is RSR, WRS, or XSR instruction. */
+
+static int
+rwx_special_register (const char *opcname)
+{
+ char ch = *opcname++;
+
+ if ((ch != 'r') && (ch != 'w') && (ch != 'x'))
+ return 0;
+ if (*opcname++ != 's')
+ return 0;
+ if (*opcname++ != 'r')
+ return 0;
+ if (*opcname++ != '.')
+ return 0;
+
+ return 1;
+}
+
+/* Classify an opcode based on what it means for Call0 prologue analysis. */
+
+static xtensa_insn_kind
+call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
+{
+ const char *opcname;
+ xtensa_insn_kind opclass = c0opc_uninteresting;
+
+ DEBUGTRACE ("call0_classify_opcode (..., opc = %d)\n", opc);
+
+ /* Get opcode name and handle special classifications. */
+
+ opcname = xtensa_opcode_name (isa, opc);
+
+ if (opcname == NULL
+ || strcasecmp (opcname, "ill") == 0
+ || strcasecmp (opcname, "ill.n") == 0)
+ opclass = c0opc_illegal;
+ else if (strcasecmp (opcname, "break") == 0
+ || strcasecmp (opcname, "break.n") == 0)
+ opclass = c0opc_break;
+ else if (strcasecmp (opcname, "entry") == 0)
+ opclass = c0opc_entry;
+ else if (strcasecmp (opcname, "rfwo") == 0)
+ opclass = c0opc_rfwo;
+ else if (strcasecmp (opcname, "rfwu") == 0)
+ opclass = c0opc_rfwu;
+ else if (xtensa_opcode_is_branch (isa, opc) > 0
+ || xtensa_opcode_is_jump (isa, opc) > 0
+ || xtensa_opcode_is_loop (isa, opc) > 0
+ || xtensa_opcode_is_call (isa, opc) > 0
+ || strcasecmp (opcname, "simcall") == 0
+ || strcasecmp (opcname, "syscall") == 0)
+ opclass = c0opc_flow;
+
+ /* Also, classify specific opcodes that need to be tracked. */
+ else if (strcasecmp (opcname, "add") == 0
+ || strcasecmp (opcname, "add.n") == 0)
+ opclass = c0opc_add;
+ else if (strcasecmp (opcname, "and") == 0)
+ opclass = c0opc_and;
+ else if (strcasecmp (opcname, "addi") == 0
+ || strcasecmp (opcname, "addi.n") == 0
+ || strcasecmp (opcname, "addmi") == 0)
+ opclass = c0opc_addi;
+ else if (strcasecmp (opcname, "sub") == 0)
+ opclass = c0opc_sub;
+ else if (strcasecmp (opcname, "mov.n") == 0
+ || strcasecmp (opcname, "or") == 0) /* Could be 'mov' asm macro. */
+ opclass = c0opc_mov;
+ else if (strcasecmp (opcname, "movi") == 0
+ || strcasecmp (opcname, "movi.n") == 0)
+ opclass = c0opc_movi;
+ else if (strcasecmp (opcname, "l32r") == 0)
+ opclass = c0opc_l32r;
+ else if (strcasecmp (opcname, "s32i") == 0
+ || strcasecmp (opcname, "s32i.n") == 0)
+ opclass = c0opc_s32i;
+ else if (strcasecmp (opcname, "l32e") == 0)
+ opclass = c0opc_l32e;
+ else if (strcasecmp (opcname, "s32e") == 0)
+ opclass = c0opc_s32e;
+ else if (rwx_special_register (opcname))
+ opclass = c0opc_rwxsr;
+
+ return opclass;
+}
+
+/* Tracks register movement/mutation for a given operation, which may
+ be within a bundle. Updates the destination register tracking info
+ accordingly. The pc is needed only for pc-relative load instructions
+ (eg. l32r). The SP register number is needed to identify stores to
+ the stack frame. Returns 0, if analysis was successful, non-zero
+ otherwise. */
+
+static int
+call0_track_op (struct gdbarch *gdbarch, xtensa_c0reg_t dst[], xtensa_c0reg_t src[],
+ xtensa_insn_kind opclass, int nods, unsigned odv[],
+ CORE_ADDR pc, int spreg, xtensa_frame_cache_t *cache)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ unsigned litbase, litaddr, litval;
+
+ switch (opclass)
+ {
+ case c0opc_addi:
+ /* 3 operands: dst, src, imm. */
+ gdb_assert (nods == 3);
+ dst[odv[0]].fr_reg = src[odv[1]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[1]].fr_ofs + odv[2];
+ break;
+ case c0opc_add:
+ /* 3 operands: dst, src1, src2. */
+ gdb_assert (nods == 3);
+ if (src[odv[1]].fr_reg == C0_CONST)
+ {
+ dst[odv[0]].fr_reg = src[odv[2]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[2]].fr_ofs + src[odv[1]].fr_ofs;
+ }
+ else if (src[odv[2]].fr_reg == C0_CONST)
+ {
+ dst[odv[0]].fr_reg = src[odv[1]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[1]].fr_ofs + src[odv[2]].fr_ofs;
+ }
+ else dst[odv[0]].fr_reg = C0_INEXP;
+ break;
+ case c0opc_and:
+ /* 3 operands: dst, src1, src2. */
+ gdb_assert (nods == 3);
+ if (cache->c0.c0_fpalign == 0)
+ {
+ /* Handle dynamic stack alignment. */
+ if ((src[odv[0]].fr_reg == spreg) && (src[odv[1]].fr_reg == spreg))
+ {
+ if (src[odv[2]].fr_reg == C0_CONST)
+ cache->c0.c0_fpalign = src[odv[2]].fr_ofs;
+ break;
+ }
+ else if ((src[odv[0]].fr_reg == spreg)
+ && (src[odv[2]].fr_reg == spreg))
+ {
+ if (src[odv[1]].fr_reg == C0_CONST)
+ cache->c0.c0_fpalign = src[odv[1]].fr_ofs;
+ break;
+ }
+ /* else fall through. */
+ }
+ if (src[odv[1]].fr_reg == C0_CONST)
+ {
+ dst[odv[0]].fr_reg = src[odv[2]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[2]].fr_ofs & src[odv[1]].fr_ofs;
+ }
+ else if (src[odv[2]].fr_reg == C0_CONST)
+ {
+ dst[odv[0]].fr_reg = src[odv[1]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[1]].fr_ofs & src[odv[2]].fr_ofs;
+ }
+ else dst[odv[0]].fr_reg = C0_INEXP;
+ break;
+ case c0opc_sub:
+ /* 3 operands: dst, src1, src2. */
+ gdb_assert (nods == 3);
+ if (src[odv[2]].fr_reg == C0_CONST)
+ {
+ dst[odv[0]].fr_reg = src[odv[1]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[1]].fr_ofs - src[odv[2]].fr_ofs;
+ }
+ else dst[odv[0]].fr_reg = C0_INEXP;
+ break;
+ case c0opc_mov:
+ /* 2 operands: dst, src [, src]. */
+ gdb_assert (nods == 2);
+ /* First, check if it's a special case of saving unaligned SP
+ to a spare register in case of dynamic stack adjustment.
+ But, only do it one time. The second time could be initializing
+ frame pointer. We don't want to overwrite the first one. */
+ if ((odv[1] == spreg) && (cache->c0.c0_old_sp == C0_INEXP))
+ cache->c0.c0_old_sp = odv[0];
+
+ dst[odv[0]].fr_reg = src[odv[1]].fr_reg;
+ dst[odv[0]].fr_ofs = src[odv[1]].fr_ofs;
+ break;
+ case c0opc_movi:
+ /* 2 operands: dst, imm. */
+ gdb_assert (nods == 2);
+ dst[odv[0]].fr_reg = C0_CONST;
+ dst[odv[0]].fr_ofs = odv[1];
+ break;
+ case c0opc_l32r:
+ /* 2 operands: dst, literal offset. */
+ gdb_assert (nods == 2);
+ /* litbase = xtensa_get_litbase (pc); can be also used. */
+ litbase = (gdbarch_tdep (gdbarch)->litbase_regnum == -1)
+ ? 0 : xtensa_read_register
+ (gdbarch_tdep (gdbarch)->litbase_regnum);
+ litaddr = litbase & 1
+ ? (litbase & ~1) + (signed)odv[1]
+ : (pc + 3 + (signed)odv[1]) & ~3;
+ litval = read_memory_integer (litaddr, 4, byte_order);
+ dst[odv[0]].fr_reg = C0_CONST;
+ dst[odv[0]].fr_ofs = litval;
+ break;
+ case c0opc_s32i:
+ /* 3 operands: value, base, offset. */
+ gdb_assert (nods == 3 && spreg >= 0 && spreg < C0_NREGS);
+ /* First, check if it's a spill for saved unaligned SP,
+ when dynamic stack adjustment was applied to this frame. */
+ if ((cache->c0.c0_fpalign != 0) /* Dynamic stack adjustment. */
+ && (odv[1] == spreg) /* SP usage indicates spill. */
+ && (odv[0] == cache->c0.c0_old_sp)) /* Old SP register spilled. */
+ cache->c0.c0_sp_ofs = odv[2];
+
+ if (src[odv[1]].fr_reg == spreg /* Store to stack frame. */
+ && (src[odv[1]].fr_ofs & 3) == 0 /* Alignment preserved. */
+ && src[odv[0]].fr_reg >= 0 /* Value is from a register. */
+ && src[odv[0]].fr_ofs == 0 /* Value hasn't been modified. */
+ && src[src[odv[0]].fr_reg].to_stk == C0_NOSTK) /* First time. */
+ {
+ /* ISA encoding guarantees alignment. But, check it anyway. */
+ gdb_assert ((odv[2] & 3) == 0);
+ dst[src[odv[0]].fr_reg].to_stk = src[odv[1]].fr_ofs + odv[2];
+ }
+ break;
+ /* If we end up inside Window Overflow / Underflow interrupt handler
+ report an error because these handlers should have been handled
+ already in a different way. */
+ case c0opc_l32e:
+ case c0opc_s32e:
+ case c0opc_rfwo:
+ case c0opc_rfwu:
+ return 1;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+/* Analyze prologue of the function at start address to determine if it uses
+ the Call0 ABI, and if so track register moves and linear modifications
+ in the prologue up to the PC or just beyond the prologue, whichever is
+ first. An 'entry' instruction indicates non-Call0 ABI and the end of the
+ prologue. The prologue may overlap non-prologue instructions but is
+ guaranteed to end by the first flow-control instruction (jump, branch,
+ call or return). Since an optimized function may move information around
+ and change the stack frame arbitrarily during the prologue, the information
+ is guaranteed valid only at the point in the function indicated by the PC.
+ May be used to skip the prologue or identify the ABI, w/o tracking.
+
+ Returns: Address of first instruction after prologue, or PC (whichever
+ is first), or 0, if decoding failed (in libisa).
+ Input args:
+ start Start address of function/prologue.
+ pc Program counter to stop at. Use 0 to continue to end of prologue.
+ If 0, avoids infinite run-on in corrupt code memory by bounding
+ the scan to the end of the function if that can be determined.
+ nregs Number of general registers to track.
+ InOut args:
+ cache Xtensa frame cache.
+
+ Note that these may produce useful results even if decoding fails
+ because they begin with default assumptions that analysis may change. */
+
+static CORE_ADDR
+call0_analyze_prologue (struct gdbarch *gdbarch,
+ CORE_ADDR start, CORE_ADDR pc,
+ int nregs, xtensa_frame_cache_t *cache)
+{
+ CORE_ADDR ia; /* Current insn address in prologue. */
+ CORE_ADDR ba = 0; /* Current address at base of insn buffer. */
+ CORE_ADDR bt; /* Current address at top+1 of insn buffer. */
+ gdb_byte ibuf[XTENSA_ISA_BSZ];/* Instruction buffer for decoding prologue. */
+ xtensa_isa isa; /* libisa ISA handle. */
+ xtensa_insnbuf ins, slot; /* libisa handle to decoded insn, slot. */
+ xtensa_format ifmt; /* libisa instruction format. */
+ int ilen, islots, is; /* Instruction length, nbr slots, current slot. */
+ xtensa_opcode opc; /* Opcode in current slot. */
+ xtensa_insn_kind opclass; /* Opcode class for Call0 prologue analysis. */
+ int nods; /* Opcode number of operands. */
+ unsigned odv[C0_MAXOPDS]; /* Operand values in order provided by libisa. */
+ xtensa_c0reg_t *rtmp; /* Register tracking info snapshot. */
+ int j; /* General loop counter. */
+ int fail = 0; /* Set non-zero and exit, if decoding fails. */
+ CORE_ADDR body_pc; /* The PC for the first non-prologue insn. */
+ CORE_ADDR end_pc; /* The PC for the lust function insn. */
+
+ struct symtab_and_line prologue_sal;
+
+ DEBUGTRACE ("call0_analyze_prologue (start = 0x%08x, pc = 0x%08x, ...)\n",
+ (int)start, (int)pc);
+
+ /* Try to limit the scan to the end of the function if a non-zero pc
+ arg was not supplied to avoid probing beyond the end of valid memory.
+ If memory is full of garbage that classifies as c0opc_uninteresting.
+ If this fails (eg. if no symbols) pc ends up 0 as it was.
+ Initialize the Call0 frame and register tracking info.
+ Assume it's Call0 until an 'entry' instruction is encountered.
+ Assume we may be in the prologue until we hit a flow control instr. */
+
+ rtmp = NULL;
+ body_pc = UINT_MAX;
+ end_pc = 0;
+
+ /* Find out, if we have an information about the prologue from DWARF. */
+ prologue_sal = find_pc_line (start, 0);
+ if (prologue_sal.line != 0) /* Found debug info. */
+ body_pc = prologue_sal.end;
+
+ /* If we are going to analyze the prologue in general without knowing about
+ the current PC, make the best assumption for the end of the prologue. */
+ if (pc == 0)
+ {
+ find_pc_partial_function (start, 0, NULL, &end_pc);
+ body_pc = std::min (end_pc, body_pc);
+ }