+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;
+
+/* Simulate L32E instruction: AT <-- ref (AS + offset). */
+static void
+execute_l32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
+{
+ int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
+ int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
+ CORE_ADDR addr = xtensa_read_register (asreg) + offset;
+ unsigned int spilled_value
+ = read_memory_unsigned_integer (addr, 4, gdbarch_byte_order (gdbarch));
+
+ if ((at == 0) && !a0_was_saved)
+ {
+ a0_saved = xtensa_read_register (atreg);
+ a0_was_saved = 1;
+ }
+ else if ((at == 7) && !a7_was_saved)
+ {
+ a7_saved = xtensa_read_register (atreg);
+ a7_was_saved = 1;
+ }
+ else if ((at == 11) && !a11_was_saved)
+ {
+ a11_saved = xtensa_read_register (atreg);
+ a11_was_saved = 1;
+ }
+
+ xtensa_write_register (atreg, spilled_value);
+}
+
+/* Simulate S32E instruction: AT --> ref (AS + offset). */
+static void
+execute_s32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
+{
+ int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
+ int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
+ CORE_ADDR addr = xtensa_read_register (asreg) + offset;
+ ULONGEST spilled_value = xtensa_read_register (atreg);
+
+ write_memory_unsigned_integer (addr, 4,
+ gdbarch_byte_order (gdbarch),
+ spilled_value);
+}
+
+#define XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN 200
+
+typedef enum
+{
+ xtWindowOverflow,
+ xtWindowUnderflow,
+ xtNoExceptionHandler
+} xtensa_exception_handler_t;
+
+/* Execute instruction stream from current PC until hitting RFWU or RFWO.
+ Return type of Xtensa Window Interrupt Handler on success. */
+static xtensa_exception_handler_t
+execute_code (struct gdbarch *gdbarch, CORE_ADDR current_pc, CORE_ADDR wb)
+{
+ 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;
+ int insn_num = 0;
+ int fail = 0;
+ void (*func) (struct gdbarch *, int, int, int, CORE_ADDR);
+
+ int at, as, offset;
+
+ /* WindowUnderflow12 = true, when inside _WindowUnderflow12. */
+ int WindowUnderflow12 = (current_pc & 0x1ff) >= 0x140;
+
+ 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;
+ ia = current_pc;
+ bt = ia;
+
+ a0_was_saved = 0;
+ a7_was_saved = 0;
+ a11_was_saved = 0;
+
+ while (insn_num++ < XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN)
+ {
+ if (ia + xtensa_isa_maxlength (isa) > bt)
+ {
+ ba = ia;
+ bt = (ba + XTENSA_ISA_BSZ);
+ if (target_read_memory (ba, ibuf, bt - ba) != 0)
+ return xtNoExceptionHandler;
+ }
+ xtensa_insnbuf_from_chars (isa, ins, &ibuf[ia-ba], 0);
+ ifmt = xtensa_format_decode (isa, ins);
+ if (ifmt == XTENSA_UNDEFINED)
+ return xtNoExceptionHandler;
+ ilen = xtensa_format_length (isa, ifmt);
+ if (ilen == XTENSA_UNDEFINED)
+ return xtNoExceptionHandler;
+ islots = xtensa_format_num_slots (isa, ifmt);
+ if (islots == XTENSA_UNDEFINED)
+ return xtNoExceptionHandler;
+ for (is = 0; is < islots; ++is)
+ {
+ if (xtensa_format_get_slot (isa, ifmt, is, ins, slot))
+ return xtNoExceptionHandler;
+ opc = xtensa_opcode_decode (isa, ifmt, is, slot);
+ if (opc == XTENSA_UNDEFINED)
+ return xtNoExceptionHandler;
+ switch (call0_classify_opcode (isa, opc))
+ {
+ case c0opc_illegal:
+ case c0opc_flow:
+ case c0opc_entry:
+ case c0opc_break:
+ /* We expect none of them here. */
+ return xtNoExceptionHandler;
+ case c0opc_l32e:
+ func = execute_l32e;
+ break;
+ case c0opc_s32e:
+ func = execute_s32e;
+ break;
+ case c0opc_rfwo: /* RFWO. */
+ /* Here, we return from WindowOverflow handler and,
+ if we stopped at the very beginning, which means
+ A0 was saved, we have to restore it now. */
+ if (a0_was_saved)
+ {
+ int arreg = arreg_number (gdbarch,
+ gdbarch_tdep (gdbarch)->a0_base,
+ wb);
+ xtensa_write_register (arreg, a0_saved);
+ }
+ return xtWindowOverflow;
+ case c0opc_rfwu: /* RFWU. */
+ /* Here, we return from WindowUnderflow handler.
+ Let's see if either A7 or A11 has to be restored. */
+ if (WindowUnderflow12)
+ {
+ if (a11_was_saved)
+ {
+ int arreg = arreg_number (gdbarch,
+ gdbarch_tdep (gdbarch)->a0_base + 11,
+ wb);
+ xtensa_write_register (arreg, a11_saved);
+ }
+ }
+ else if (a7_was_saved)
+ {
+ int arreg = arreg_number (gdbarch,
+ gdbarch_tdep (gdbarch)->a0_base + 7,
+ wb);
+ xtensa_write_register (arreg, a7_saved);
+ }
+ return xtWindowUnderflow;
+ default: /* Simply skip this insns. */
+ continue;
+ }
+
+ /* Decode arguments for L32E / S32E and simulate their execution. */
+ if ( xtensa_opcode_num_operands (isa, opc) != 3 )
+ return xtNoExceptionHandler;
+ if (xtensa_operand_get_field (isa, opc, 0, ifmt, is, slot, &at))
+ return xtNoExceptionHandler;
+ if (xtensa_operand_decode (isa, opc, 0, &at))
+ return xtNoExceptionHandler;
+ if (xtensa_operand_get_field (isa, opc, 1, ifmt, is, slot, &as))
+ return xtNoExceptionHandler;
+ if (xtensa_operand_decode (isa, opc, 1, &as))
+ return xtNoExceptionHandler;
+ if (xtensa_operand_get_field (isa, opc, 2, ifmt, is, slot, &offset))
+ return xtNoExceptionHandler;
+ if (xtensa_operand_decode (isa, opc, 2, &offset))
+ return xtNoExceptionHandler;
+
+ (*func) (gdbarch, at, as, offset, wb);
+ }
+
+ ia += ilen;
+ }
+ return xtNoExceptionHandler;
+}
+
+/* Handle Window Overflow / Underflow exception frames. */
+
+static void
+xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
+ xtensa_frame_cache_t *cache,
+ CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ CORE_ADDR ps, wb, ws, ra;
+ int epc1_regnum, i, regnum;
+ xtensa_exception_handler_t eh_type;
+
+ /* Read PS, WB, and WS from the hardware. Note that PS register
+ must be present, if Windowed ABI is supported. */
+ ps = xtensa_read_register (gdbarch_ps_regnum (gdbarch));
+ wb = xtensa_read_register (gdbarch_tdep (gdbarch)->wb_regnum);
+ ws = xtensa_read_register (gdbarch_tdep (gdbarch)->ws_regnum);
+
+ /* Execute all the remaining instructions from Window Interrupt Handler
+ by simulating them on the remote protocol level. On return, set the
+ type of Xtensa Window Interrupt Handler, or report an error. */
+ eh_type = execute_code (gdbarch, pc, wb);
+ if (eh_type == xtNoExceptionHandler)
+ error (_("\
+Unable to decode Xtensa Window Interrupt Handler's code."));
+
+ cache->ps = ps ^ PS_EXC; /* Clear the exception bit in PS. */
+ cache->call0 = 0; /* It's Windowed ABI. */
+
+ /* All registers for the cached frame will be alive. */
+ for (i = 0; i < XTENSA_NUM_SAVED_AREGS; i++)
+ cache->wd.aregs[i] = -1;
+
+ if (eh_type == xtWindowOverflow)
+ cache->wd.ws = ws ^ (1 << wb);
+ else /* eh_type == xtWindowUnderflow. */
+ cache->wd.ws = ws | (1 << wb);
+
+ cache->wd.wb = (ps & 0xf00) >> 8; /* Set WB to OWB. */
+ regnum = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base,
+ cache->wd.wb);
+ ra = xtensa_read_register (regnum);
+ cache->wd.callsize = WINSIZE (ra);
+ cache->prev_sp = xtensa_read_register (regnum + 1);
+ /* Set regnum to a frame pointer of the frame being cached. */
+ regnum = xtensa_scan_prologue (gdbarch, pc);
+ regnum = arreg_number (gdbarch,
+ gdbarch_tdep (gdbarch)->a0_base + regnum,
+ cache->wd.wb);
+ cache->base = get_frame_register_unsigned (this_frame, regnum);
+
+ /* Read PC of interrupted function from EPC1 register. */
+ epc1_regnum = xtensa_find_register_by_name (gdbarch,"epc1");
+ if (epc1_regnum < 0)
+ error(_("Unable to read Xtensa register EPC1"));
+ cache->ra = xtensa_read_register (epc1_regnum);
+ cache->pc = get_frame_func (this_frame);
+}
+
+
+/* Skip function prologue.
+
+ Return the pc of the first instruction after prologue. GDB calls this to
+ find the address of the first line of the function or (if there is no line
+ number information) to skip the prologue for planting breakpoints on
+ function entries. Use debug info (if present) or prologue analysis to skip
+ the prologue to achieve reliable debugging behavior. For windowed ABI,
+ only the 'entry' instruction is skipped. It is not strictly necessary to
+ skip the prologue (Call0) or 'entry' (Windowed) because xt-gdb knows how to
+ backtrace at any point in the prologue, however certain potential hazards
+ are avoided and a more "normal" debugging experience is ensured by
+ skipping the prologue (can be disabled by defining DONT_SKIP_PROLOG).
+ For example, if we don't skip the prologue:
+ - Some args may not yet have been saved to the stack where the debug
+ info expects to find them (true anyway when only 'entry' is skipped);
+ - Software breakpoints ('break' instrs) may not have been unplanted
+ when the prologue analysis is done on initializing the frame cache,
+ and breaks in the prologue will throw off the analysis.