2011-03-10 Maxim Grigoriev <maxim2405@gmail.com>
authorMaxim Grigoriev <maxim2405@gmail.com>
Fri, 11 Mar 2011 00:21:42 +0000 (00:21 +0000)
committerMaxim Grigoriev <maxim2405@gmail.com>
Fri, 11 Mar 2011 00:21:42 +0000 (00:21 +0000)
* xtensa-tdep.c (xtensa_c0reg_t): Update comments.
(xtensa_call0_frame_cache_t): Update comments. New fields added.
(xtensa_alloc_frame_cache): Add initialization for new fields.
(xtensa_frame_cache): Change the way how call0_frame_cache () is called.
(warning_once): New function.
(xtensa_insn_kind): New item c0opc_and.
(call0_classify_opcode): Add the case for AND instruction.
(call0_track_op): Change arguments. New local variable litbase. Add the
case to handle c0opc_and. Update algorithms for c0opc_mov, c0opc_l32r,
c0opc_s32i to take into account dynamic stack adjustments in prologue.
Add cases for c0opc_l32e, c0opc_s32e, c0opc_rfwo, c0opc_rfwu.
(call0_analyze_prologue): Update the comments. Change arguments.
Add the variety of updates to handle extended prologues, which now can
conduct dynamic stack adjustments.
(call0_frame_cache): Likewise.
(xtensa_skip_prologue): Update call0_analyze_prologue () function call.
(xtensa_gdbarch_init): Initialize xtensa_session_once_reported.

gdb/ChangeLog
gdb/xtensa-tdep.c

index 19a767eaa30914a1cad33fd99df4330cc56dc2c3..e795c5244b4b466a59e34350846495ebfc7e163d 100644 (file)
@@ -1,3 +1,23 @@
+2011-03-10  Maxim Grigoriev  <maxim2405@gmail.com>
+
+       * xtensa-tdep.c (xtensa_c0reg_t): Update comments.
+       (xtensa_call0_frame_cache_t): Update comments. New fields added.
+       (xtensa_alloc_frame_cache): Add initialization for new fields.
+       (xtensa_frame_cache): Change the way how call0_frame_cache () is called.
+       (warning_once): New function.
+       (xtensa_insn_kind): New item c0opc_and.
+       (call0_classify_opcode): Add the case for AND instruction.
+       (call0_track_op): Change arguments. New local variable litbase. Add the
+       case to handle c0opc_and. Update algorithms for c0opc_mov, c0opc_l32r,
+       c0opc_s32i to take into account dynamic stack adjustments in prologue.
+       Add cases for c0opc_l32e, c0opc_s32e, c0opc_rfwo, c0opc_rfwu.
+       (call0_analyze_prologue): Update the comments. Change arguments.
+       Add the variety of updates to handle extended prologues, which now can
+       conduct dynamic stack adjustments.
+       (call0_frame_cache): Likewise.
+       (xtensa_skip_prologue): Update call0_analyze_prologue () function call.
+       (xtensa_gdbarch_init): Initialize xtensa_session_once_reported.
+
 2011-03-10  Michael Snyder  <msnyder@vmware.com>
 
        * tracepoint.c (cmd_qtv): Discard unused value 'packet'.
@@ -57,7 +77,7 @@
 
 2011-03-09  Maxim Grigoriev  <maxim2405@gmail.com>
 
-       * xtensa-tdep.c (xtensa_read_register: Add comment.
+       * xtensa-tdep.c (xtensa_read_register): Add comment.
        (xtensa_write_register): Likewise.
        (xtensa_hextochar): Add comment and update to match coding conventions.
        (xtensa_frame_cache, xtensa_return_value): Follow coding conventions.
index 26335d8ba31322b6b029d84055bd666cad778b55..f6dacf83fb3ed6c367c53787e32a7dbab8d5ed43 100644 (file)
@@ -982,24 +982,30 @@ extern xtensa_isa xtensa_default_isa;
 
 typedef struct xtensa_c0reg
 {
-    int            fr_reg;     /* original register from which register content
-                          is derived, or C0_CONST, or C0_INEXP.  */
-    int            fr_ofs;     /* constant offset from reg, or immediate value.  */
-    int            to_stk;     /* offset from original SP to register (4-byte
-                          aligned), or C0_NOSTK if register has not
-                          been saved.  */
+  int fr_reg;  /* original register from which register content
+                 is derived, or C0_CONST, or C0_INEXP.  */
+  int fr_ofs;  /* constant offset from reg, or immediate value.  */
+  int to_stk;  /* offset from original SP to register (4-byte aligned),
+                 or C0_NOSTK if register has not been saved.  */
 } xtensa_c0reg_t;
 
-
 /* Frame cache part for Call0 ABI.  */
 typedef struct xtensa_call0_frame_cache
 {
-  int c0_frmsz;                                /* Stack frame size.  */
-  int c0_hasfp;                                /* Current frame uses frame
-                                          pointer.  */
-  int fp_regnum;                       /* A-register used as FP.  */
-  int c0_fp;                           /* Actual value of frame pointer.  */
-  xtensa_c0reg_t c0_rt[C0_NREGS];      /* Register tracking information.  */
+  int c0_frmsz;                           /* Stack frame size.  */
+  int c0_hasfp;                           /* Current frame uses frame pointer.  */
+  int fp_regnum;                  /* A-register used as FP.  */
+  int c0_fp;                      /* Actual value of frame pointer.  */
+  int c0_fpalign;                 /* Dinamic adjustment for the stack
+                                     pointer. It's an AND mask. Zero,
+                                     if alignment was not adjusted.  */
+  int c0_old_sp;                  /* In case of dynamic adjustment, it is
+                                     a register holding unaligned sp. 
+                                     C0_INEXP, when undefined.  */
+  int c0_sp_ofs;                  /* If "c0_old_sp" was spilled it's a
+                                     stack offset. C0_NOSTK otherwise.  */
+                                          
+  xtensa_c0reg_t c0_rt[C0_NREGS];  /* Register tracking information.  */
 } xtensa_call0_frame_cache_t;
 
 typedef struct xtensa_frame_cache
@@ -1040,6 +1046,9 @@ xtensa_alloc_frame_cache (int windowed)
       cache->c0.c0_hasfp  =  0;
       cache->c0.fp_regnum = -1;
       cache->c0.c0_fp     = -1;
+      cache->c0.c0_fpalign =  0;
+      cache->c0.c0_old_sp  =  C0_INEXP;
+      cache->c0.c0_sp_ofs  =  C0_NOSTK;
 
       for (i = 0; i < C0_NREGS; i++)
        {
@@ -1261,8 +1270,7 @@ done:
 
 static void
 call0_frame_cache (struct frame_info *this_frame,
-                  xtensa_frame_cache_t *cache,
-                  CORE_ADDR pc, CORE_ADDR litbase);
+                  xtensa_frame_cache_t *cache, CORE_ADDR pc);
 
 static void
 xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
@@ -1408,11 +1416,7 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
     }
   else /* Call0 framework.  */
     {
-      unsigned int litbase_regnum = gdbarch_tdep (gdbarch)->litbase_regnum;
-      CORE_ADDR litbase = (litbase_regnum == -1)
-       ? 0 : get_frame_register_unsigned (this_frame, litbase_regnum);
-
-      call0_frame_cache (this_frame, cache, pc, litbase);
+      call0_frame_cache (this_frame, cache, pc);  
       fp_regnum = cache->c0.fp_regnum;
     }
 
@@ -1421,6 +1425,22 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
   return cache;
 }
 
+static int xtensa_session_once_reported = 1;
+
+/* Report a problem with prologue analysis while doing backtracing.
+   But, do it only once to avoid annoyng repeated messages.  */
+
+static inline void warning_once ()
+{
+  if (xtensa_session_once_reported == 0)
+    warning (_("\
+\nUnrecognised function prologue. Stack trace cannot be resolved. \
+This message will not be repeated in this session.\n"));
+
+  xtensa_session_once_reported = 1;
+}
+
+
 static void
 xtensa_frame_this_id (struct frame_info *this_frame,
                      void **this_cache,
@@ -2088,6 +2108,7 @@ typedef enum
   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.  */
@@ -2159,6 +2180,8 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
   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)
@@ -2190,16 +2213,16 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
    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.  */
+   the stack frame.  Returns 0, if analysis was succesfull, non-zero
+   otherwise.  */
 
-static void
-call0_track_op (struct gdbarch *gdbarch,
-               xtensa_c0reg_t dst[], xtensa_c0reg_t src[],
+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, CORE_ADDR litbase, int spreg)
+               CORE_ADDR pc, int spreg, xtensa_frame_cache_t *cache)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  unsigned litaddr, litval;
+  unsigned litbase, litaddr, litval;
 
   switch (opclass)
     {
@@ -2224,6 +2247,39 @@ call0_track_op (struct gdbarch *gdbarch,
        }
       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);
@@ -2237,6 +2293,13 @@ call0_track_op (struct gdbarch *gdbarch,
     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;
@@ -2249,6 +2312,10 @@ call0_track_op (struct gdbarch *gdbarch,
     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;
@@ -2259,6 +2326,13 @@ call0_track_op (struct gdbarch *gdbarch,
     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.  */
@@ -2270,20 +2344,29 @@ call0_track_op (struct gdbarch *gdbarch,
          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:
-       gdb_assert_not_reached ("unexpected instruction kind");
+      return 1;
     }
+  return 0;
 }
 
-/* Analyze prologue of the function at start address to determine if it uses 
+/* 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.
+   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 
@@ -2293,21 +2376,17 @@ call0_track_op (struct gdbarch *gdbarch,
       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 (size of rt[] array).
+      nregs   Number of general registers to track.
    InOut args:
-      rt[]    Array[nregs] of xtensa_c0reg structures for register tracking info.
-             If NULL, registers are not tracked.
-   Output args:
-      call0   If != NULL, *call0 is set non-zero if Call0 ABI used, else 0
-             (more accurately, non-zero until 'entry' insn is encountered).
+      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, CORE_ADDR litbase,
-                       int nregs, xtensa_c0reg_t rt[], int *call0)
+                       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.  */
@@ -2359,15 +2438,8 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
   else
     body_pc = min (pc, body_pc);
 
-  if (call0 != NULL)
-      *call0 = 1;
-
-  if (rt != NULL)
-    {
-      rtmp = (xtensa_c0reg_t*) alloca(nregs * sizeof(xtensa_c0reg_t));
-      /* rt is already initialized in xtensa_alloc_frame_cache().  */
-    }
-  else nregs = 0;
+  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);
@@ -2386,9 +2458,8 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
         {
          ba = ia;
          bt = (ba + XTENSA_ISA_BSZ) < body_pc ? ba + XTENSA_ISA_BSZ : body_pc;
-         read_memory (ba, ibuf, bt - ba);
-         /* If there is a memory reading error read_memory () will report it
-            and then throw an exception, stopping command execution.  */
+         if (target_read_memory (ba, ibuf, bt - ba) != 0 )
+           error (_("Unable to read target memory ..."));
        }
 
       /* Decode format information.  */
@@ -2418,7 +2489,7 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
         register changes do not take effect within this bundle.  */
 
       for (j = 0; j < nregs; ++j)
-       rtmp[j] = rt[j];
+       rtmp[j] = cache->c0.c0_rt[j];
 
       for (is = 0; is < islots; ++is)
         {
@@ -2429,8 +2500,7 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
            goto done;
 
          opc = xtensa_opcode_decode (isa, ifmt, is, slot);
-         DEBUGVERB ("[call0_analyze_prologue] instr "
-                    "addr = 0x%08x, opc = %d\n", 
+         DEBUGVERB ("[call0_analyze_prologue] instr addr = 0x%08x, opc = %d\n", 
                     (unsigned)ia, opc);
          if (opc == XTENSA_UNDEFINED) 
            opclass = c0opc_illegal;
@@ -2449,23 +2519,20 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
            case c0opc_uninteresting:
              continue;
 
-           case c0opc_flow:
+           case c0opc_flow:  /* Flow control instructions stop analysis.  */
+           case c0opc_rwxsr: /* RSR, WSR, XSR instructions stop analysis.  */
              goto done;
 
            case c0opc_entry:
-             if (call0 != NULL)
-               *call0 = 0;
+             cache->call0 = 0;
              ia += ilen;               /* Skip over 'entry' insn.  */
              goto done;
 
            default:
-             if (call0 != NULL)
-               *call0 = 1;
+             cache->call0 = 1;
            }
 
          /* Only expected opcodes should get this far.  */
-         if (rt == NULL)
-           continue;
 
          /* Extract and decode the operands.  */
          nods = xtensa_opcode_num_operands (isa, opc);
@@ -2491,7 +2558,13 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
          if (opclass == c0opc_mov && nods == 3)
            {
              if (odv[2] == odv[1])
-               nods = 2;
+               {
+                 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;
@@ -2500,8 +2573,10 @@ call0_analyze_prologue (struct gdbarch *gdbarch,
            }
 
          /* Track register movement and modification for this operation.  */
-         call0_track_op (gdbarch, rt, rtmp, opclass,
-                         nods, odv, ia, litbase, 1);
+         fail = call0_track_op (gdbarch, cache->c0.c0_rt, rtmp,
+                                opclass, nods, odv, ia, 1, cache);
+         if (fail)
+           goto done;
        }
     }
 done:
@@ -2516,39 +2591,38 @@ done:
 
 static void
 call0_frame_cache (struct frame_info *this_frame,
-                  xtensa_frame_cache_t *cache,
-                  CORE_ADDR pc, CORE_ADDR litbase)
+                  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_hasfp, c0_frmsz, prev_sp, to_stk;
+  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, litbase,
-                                       C0_NREGS,
-                                       &cache->c0.c0_rt[0],
-                                       &cache->call0);
+      body_pc = call0_analyze_prologue (gdbarch, start_pc, pc, C0_NREGS, cache);
 
       if (body_pc == XTENSA_ISA_BADPC)
-       error (_("Xtensa-specific internal error: CALL0 prologue \
-analysis failed in this frame. GDB command execution stopped."));
+       {
+         warning_once ();
+         ra = 0;
+         goto finish_frame_analysis;
+       }
     }
   
-  sp = get_frame_register_unsigned
-    (this_frame, gdbarch_tdep (gdbarch)->a0_base + 1);
-  fp = sp; /* Assume FP == SP until proven otherwise.  */
-
   /* 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 know no calls have yet taken place, so we can almost
+     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)
@@ -2571,7 +2645,35 @@ analysis failed in this frame. GDB command execution stopped."));
       start_pc = pc;
    }
 
-  prev_sp = fp + c0_frmsz;
+  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.  */
@@ -2579,8 +2681,6 @@ analysis failed in this frame. GDB command execution stopped."));
     {
       fp = get_frame_register_unsigned (this_frame, fp_regnum);
 
-      /* Recalculate previous SP.  */
-      prev_sp = fp + c0_frmsz;
       /* Update the stack frame size.  */
       c0_frmsz += fp - sp;
     }
@@ -2597,23 +2697,21 @@ analysis failed in this frame. GDB command execution stopped."));
   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.  Such a function will
-        clear it's RA (A0) register to 0 in the prologue instead of
-        saving its original value.  */
+      /* 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.  */
+      /* 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; 
@@ -2631,6 +2729,7 @@ analysis failed in this frame. GDB command execution stopped."));
       else ra = 0;
     }
   
+ finish_frame_analysis:
   cache->pc = start_pc;
   cache->ra = ra;
   /* RA == 0 marks the outermost frame.  Do not go past it.  */
@@ -2971,7 +3070,8 @@ xtensa_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
     }
 
   /* No debug line info.  Analyze prologue for Call0 or simply skip ENTRY.  */
-  body_pc = call0_analyze_prologue (gdbarch, start_pc, 0, 0, 0, NULL, NULL);
+  body_pc = call0_analyze_prologue (gdbarch, start_pc, 0, 0,
+                                   xtensa_alloc_frame_cache (0));
   return body_pc != 0 ? body_pc : start_pc;
 }
 
@@ -3122,6 +3222,7 @@ xtensa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   /* Verify our configuration.  */
   xtensa_verify_config (gdbarch);
+  xtensa_session_once_reported = 0;
 
   /* Pseudo-Register read/write.  */
   set_gdbarch_pseudo_register_read (gdbarch, xtensa_pseudo_register_read);