+/* Call0 ABI support routines. */
+
+/* Return true, if PC points to "ret" or "ret.n". */
+
+static int
+call0_ret (CORE_ADDR start_pc, CORE_ADDR finish_pc)
+{
+#define RETURN_RET goto done
+ xtensa_isa isa;
+ xtensa_insnbuf ins, slot;
+ char ibuf[XTENSA_ISA_BSZ];
+ CORE_ADDR ia, bt, ba;
+ xtensa_format ifmt;
+ int ilen, islots, is;
+ xtensa_opcode opc;
+ const char *opcname;
+ int found_ret = 0;
+
+ isa = xtensa_default_isa;
+ gdb_assert (XTENSA_ISA_BSZ >= xtensa_isa_maxlength (isa));
+ ins = xtensa_insnbuf_alloc (isa);
+ slot = xtensa_insnbuf_alloc (isa);
+ ba = 0;
+
+ for (ia = start_pc, bt = ia; ia < finish_pc ; ia += ilen)
+ {
+ if (ia + xtensa_isa_maxlength (isa) > bt)
+ {
+ ba = ia;
+ bt = (ba + XTENSA_ISA_BSZ) < finish_pc
+ ? ba + XTENSA_ISA_BSZ : finish_pc;
+ if (target_read_memory (ba, ibuf, bt - ba) != 0 )
+ RETURN_RET;
+ }
+
+ xtensa_insnbuf_from_chars (isa, ins, &ibuf[ia-ba], 0);
+ ifmt = xtensa_format_decode (isa, ins);
+ if (ifmt == XTENSA_UNDEFINED)
+ RETURN_RET;
+ ilen = xtensa_format_length (isa, ifmt);
+ if (ilen == XTENSA_UNDEFINED)
+ RETURN_RET;
+ islots = xtensa_format_num_slots (isa, ifmt);
+ if (islots == XTENSA_UNDEFINED)
+ RETURN_RET;
+
+ for (is = 0; is < islots; ++is)
+ {
+ if (xtensa_format_get_slot (isa, ifmt, is, ins, slot))
+ RETURN_RET;
+
+ opc = xtensa_opcode_decode (isa, ifmt, is, slot);
+ if (opc == XTENSA_UNDEFINED)
+ RETURN_RET;
+
+ opcname = xtensa_opcode_name (isa, opc);
+
+ if ((strcasecmp (opcname, "ret.n") == 0)
+ || (strcasecmp (opcname, "ret") == 0))
+ {
+ found_ret = 1;
+ RETURN_RET;
+ }
+ }
+ }
+ 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 succesfull, 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. */
+ char 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.
+ Intialize 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 assumtion for the end of the prologue. */
+ if (pc == 0)
+ {
+ find_pc_partial_function (start, 0, NULL, &end_pc);
+ body_pc = min (end_pc, body_pc);
+ }
+ else
+ body_pc = min (pc, body_pc);
+
+ cache->call0 = 1;
+ rtmp = (xtensa_c0reg_t*) alloca(nregs * sizeof(xtensa_c0reg_t));
+
+ if (!xtensa_default_isa)
+ xtensa_default_isa = xtensa_isa_init (0, 0);
+ isa = xtensa_default_isa;
+ gdb_assert (XTENSA_ISA_BSZ >= xtensa_isa_maxlength (isa));
+ ins = xtensa_insnbuf_alloc (isa);
+ slot = xtensa_insnbuf_alloc (isa);
+
+ for (ia = start, bt = ia; ia < body_pc ; ia += ilen)
+ {
+ /* (Re)fill instruction buffer from memory if necessary, but do not
+ read memory beyond PC to be sure we stay within text section
+ (this protection only works if a non-zero pc is supplied). */
+
+ if (ia + xtensa_isa_maxlength (isa) > bt)
+ {
+ ba = ia;
+ bt = (ba + XTENSA_ISA_BSZ) < body_pc ? ba + XTENSA_ISA_BSZ : body_pc;
+ if (target_read_memory (ba, ibuf, bt - ba) != 0 )
+ error (_("Unable to read target memory ..."));
+ }
+
+ /* Decode format information. */
+
+ xtensa_insnbuf_from_chars (isa, ins, &ibuf[ia-ba], 0);
+ ifmt = xtensa_format_decode (isa, ins);
+ if (ifmt == XTENSA_UNDEFINED)
+ {
+ fail = 1;
+ goto done;
+ }
+ ilen = xtensa_format_length (isa, ifmt);
+ if (ilen == XTENSA_UNDEFINED)
+ {
+ fail = 1;
+ goto done;
+ }
+ islots = xtensa_format_num_slots (isa, ifmt);
+ if (islots == XTENSA_UNDEFINED)
+ {
+ fail = 1;
+ goto done;
+ }
+
+ /* Analyze a bundle or a single instruction, using a snapshot of
+ the register tracking info as input for the entire bundle so that
+ register changes do not take effect within this bundle. */
+
+ for (j = 0; j < nregs; ++j)
+ rtmp[j] = cache->c0.c0_rt[j];
+
+ for (is = 0; is < islots; ++is)
+ {
+ /* Decode a slot and classify the opcode. */
+
+ fail = xtensa_format_get_slot (isa, ifmt, is, ins, slot);
+ if (fail)
+ goto done;
+
+ opc = xtensa_opcode_decode (isa, ifmt, is, slot);
+ DEBUGVERB ("[call0_analyze_prologue] instr addr = 0x%08x, opc = %d\n",
+ (unsigned)ia, opc);
+ if (opc == XTENSA_UNDEFINED)
+ opclass = c0opc_illegal;
+ else
+ opclass = call0_classify_opcode (isa, opc);
+
+ /* Decide whether to track this opcode, ignore it, or bail out. */
+
+ switch (opclass)
+ {
+ case c0opc_illegal:
+ case c0opc_break:
+ fail = 1;
+ goto done;
+
+ case c0opc_uninteresting:
+ continue;
+
+ case c0opc_flow: /* Flow control instructions stop analysis. */
+ case c0opc_rwxsr: /* RSR, WSR, XSR instructions stop analysis. */
+ goto done;
+
+ case c0opc_entry:
+ cache->call0 = 0;
+ ia += ilen; /* Skip over 'entry' insn. */
+ goto done;
+
+ default:
+ cache->call0 = 1;
+ }
+
+ /* Only expected opcodes should get this far. */
+
+ /* Extract and decode the operands. */
+ nods = xtensa_opcode_num_operands (isa, opc);
+ if (nods == XTENSA_UNDEFINED)
+ {
+ fail = 1;
+ goto done;
+ }
+
+ for (j = 0; j < nods && j < C0_MAXOPDS; ++j)
+ {
+ fail = xtensa_operand_get_field (isa, opc, j, ifmt,
+ is, slot, &odv[j]);
+ if (fail)
+ goto done;
+
+ fail = xtensa_operand_decode (isa, opc, j, &odv[j]);
+ if (fail)
+ goto done;
+ }
+
+ /* Check operands to verify use of 'mov' assembler macro. */
+ if (opclass == c0opc_mov && nods == 3)
+ {
+ if (odv[2] == odv[1])
+ {
+ nods = 2;
+ if ((odv[0] == 1) && (odv[1] != 1))
+ /* OR A1, An, An , where n != 1.
+ This means we are inside epilogue already. */
+ goto done;
+ }
+ else
+ {
+ opclass = c0opc_uninteresting;
+ continue;
+ }
+ }
+
+ /* Track register movement and modification for this operation. */
+ fail = call0_track_op (gdbarch, cache->c0.c0_rt, rtmp,
+ opclass, nods, odv, ia, 1, cache);
+ if (fail)
+ goto done;
+ }
+ }
+done:
+ DEBUGVERB ("[call0_analyze_prologue] stopped at instr addr 0x%08x, %s\n",
+ (unsigned)ia, fail ? "failed" : "succeeded");
+ xtensa_insnbuf_free(isa, slot);
+ xtensa_insnbuf_free(isa, ins);
+ return fail ? XTENSA_ISA_BADPC : ia;
+}
+
+/* Initialize frame cache for the current frame in CALL0 ABI. */
+
+static void
+call0_frame_cache (struct frame_info *this_frame,
+ xtensa_frame_cache_t *cache, CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR start_pc; /* The beginning of the function. */
+ CORE_ADDR body_pc=UINT_MAX; /* PC, where prologue analysis stopped. */
+ CORE_ADDR sp, fp, ra;
+ int fp_regnum = C0_SP, c0_hasfp = 0, c0_frmsz = 0, prev_sp = 0, to_stk;
+
+ sp = get_frame_register_unsigned
+ (this_frame, gdbarch_tdep (gdbarch)->a0_base + 1);
+ fp = sp; /* Assume FP == SP until proven otherwise. */
+
+ /* Find the beginning of the prologue of the function containing the PC
+ and analyze it up to the PC or the end of the prologue. */
+
+ if (find_pc_partial_function (pc, NULL, &start_pc, NULL))
+ {
+ body_pc = call0_analyze_prologue (gdbarch, start_pc, pc, C0_NREGS, cache);
+
+ if (body_pc == XTENSA_ISA_BADPC)
+ {
+ warning_once ();
+ ra = 0;
+ goto finish_frame_analysis;
+ }
+ }
+
+ /* Get the frame information and FP (if used) at the current PC.
+ If PC is in the prologue, the prologue analysis is more reliable
+ than DWARF info. We don't not know for sure, if PC is in the prologue,
+ but we do know no calls have yet taken place, so we can almost
+ certainly rely on the prologue analysis. */
+
+ if (body_pc <= pc)
+ {
+ /* Prologue analysis was successful up to the PC.
+ It includes the cases when PC == START_PC. */
+ c0_hasfp = cache->c0.c0_rt[C0_FP].fr_reg == C0_SP;
+ /* c0_hasfp == true means there is a frame pointer because
+ we analyzed the prologue and found that cache->c0.c0_rt[C0_FP]
+ was derived from SP. Otherwise, it would be C0_FP. */
+ fp_regnum = c0_hasfp ? C0_FP : C0_SP;
+ c0_frmsz = - cache->c0.c0_rt[fp_regnum].fr_ofs;
+ fp_regnum += gdbarch_tdep (gdbarch)->a0_base;
+ }
+ else /* No data from the prologue analysis. */
+ {
+ c0_hasfp = 0;
+ fp_regnum = gdbarch_tdep (gdbarch)->a0_base + C0_SP;
+ c0_frmsz = 0;
+ start_pc = pc;
+ }
+
+ if (cache->c0.c0_fpalign)
+ {
+ /* This frame has a special prologue with a dynamic stack adjustment
+ to force an alignment, which is bigger than standard 16 bytes. */
+
+ CORE_ADDR unaligned_sp;
+
+ if (cache->c0.c0_old_sp == C0_INEXP)
+ /* This can't be. Prologue code should be consistent.
+ Unaligned stack pointer should be saved in a spare register. */
+ {
+ warning_once ();
+ ra = 0;
+ goto finish_frame_analysis;
+ }
+
+ if (cache->c0.c0_sp_ofs == C0_NOSTK)
+ /* Saved unaligned value of SP is kept in a register. */
+ unaligned_sp = get_frame_register_unsigned
+ (this_frame, gdbarch_tdep (gdbarch)->a0_base + cache->c0.c0_old_sp);
+ else
+ /* Get the value from stack. */
+ unaligned_sp = (CORE_ADDR)
+ read_memory_integer (fp + cache->c0.c0_sp_ofs, 4, byte_order);
+
+ prev_sp = unaligned_sp + c0_frmsz;
+ }
+ else
+ prev_sp = fp + c0_frmsz;
+
+ /* Frame size from debug info or prologue tracking does not account for
+ alloca() and other dynamic allocations. Adjust frame size by FP - SP. */
+ if (c0_hasfp)
+ {
+ fp = get_frame_register_unsigned (this_frame, fp_regnum);
+
+ /* Update the stack frame size. */
+ c0_frmsz += fp - sp;
+ }
+
+ /* Get the return address (RA) from the stack if saved,
+ or try to get it from a register. */
+
+ to_stk = cache->c0.c0_rt[C0_RA].to_stk;
+ if (to_stk != C0_NOSTK)
+ ra = (CORE_ADDR)
+ read_memory_integer (sp + c0_frmsz + cache->c0.c0_rt[C0_RA].to_stk,
+ 4, byte_order);
+
+ else if (cache->c0.c0_rt[C0_RA].fr_reg == C0_CONST
+ && cache->c0.c0_rt[C0_RA].fr_ofs == 0)
+ {
+ /* Special case for terminating backtrace at a function that wants to
+ be seen as the outermost one. Such a function will clear it's RA (A0)
+ register to 0 in the prologue instead of saving its original value. */
+ ra = 0;
+ }
+ else
+ {
+ /* RA was copied to another register or (before any function call) may
+ still be in the original RA register. This is not always reliable:
+ even in a leaf function, register tracking stops after prologue, and
+ even in prologue, non-prologue instructions (not tracked) may overwrite
+ RA or any register it was copied to. If likely in prologue or before
+ any call, use retracking info and hope for the best (compiler should
+ have saved RA in stack if not in a leaf function). If not in prologue,
+ too bad. */
+
+ int i;
+ for (i = 0;
+ (i < C0_NREGS)
+ && (i == C0_RA || cache->c0.c0_rt[i].fr_reg != C0_RA);
+ ++i);
+ if (i >= C0_NREGS && cache->c0.c0_rt[C0_RA].fr_reg == C0_RA)
+ i = C0_RA;
+ if (i < C0_NREGS)
+ {
+ ra = get_frame_register_unsigned
+ (this_frame,
+ gdbarch_tdep (gdbarch)->a0_base + cache->c0.c0_rt[i].fr_reg);
+ }
+ else ra = 0;
+ }
+
+ finish_frame_analysis:
+ cache->pc = start_pc;
+ cache->ra = ra;
+ /* RA == 0 marks the outermost frame. Do not go past it. */
+ cache->prev_sp = (ra != 0) ? prev_sp : 0;
+ cache->c0.fp_regnum = fp_regnum;
+ cache->c0.c0_frmsz = c0_frmsz;
+ cache->c0.c0_hasfp = c0_hasfp;
+ cache->c0.c0_fp = fp;
+}
+
+static CORE_ADDR a0_saved;
+static CORE_ADDR a7_saved;
+static CORE_ADDR a11_saved;
+static int a0_was_saved;
+static int a7_was_saved;
+static int a11_was_saved;