/* Frameless until proven otherwise. */
cache->frameless_p = 1;
-
+ cache->frame_offset = 0;
+ cache->saved_regs_mask = 0;
+ cache->copied_regs_mask = 0;
cache->struct_return_p = 0;
return cache;
return start_pc;
}
+/* Record the effect of a SAVE instruction on CACHE. */
+
+void
+sparc_record_save_insn (struct sparc_frame_cache *cache)
+{
+ /* The frame is set up. */
+ cache->frameless_p = 0;
+
+ /* The frame pointer contains the CFA. */
+ cache->frame_offset = 0;
+
+ /* The `local' and `in' registers are all saved. */
+ cache->saved_regs_mask = 0xffff;
+
+ /* The `out' registers are all renamed. */
+ cache->copied_regs_mask = 0xff;
+}
+
+/* Do a full analysis of the prologue at PC and update CACHE accordingly.
+ Bail out early if CURRENT_PC is reached. Return the address where
+ the analysis stopped.
+
+ We handle both the traditional register window model and the single
+ register window (aka flat) model. */
+
CORE_ADDR
sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc,
CORE_ADDR current_pc, struct sparc_frame_cache *cache)
insn = sparc_fetch_instruction (pc);
+ /* Recognize store insns and record their sources. */
+ while (X_OP (insn) == 3
+ && (X_OP3 (insn) == 0x4 /* stw */
+ || X_OP3 (insn) == 0x7 /* std */
+ || X_OP3 (insn) == 0xe) /* stx */
+ && X_RS1 (insn) == SPARC_SP_REGNUM)
+ {
+ int regnum = X_RD (insn);
+
+ /* Recognize stores into the corresponding stack slots. */
+ if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM
+ && ((X_I (insn)
+ && X_SIMM13 (insn) == (X_OP3 (insn) == 0xe
+ ? (regnum - SPARC_L0_REGNUM) * 8 + BIAS
+ : (regnum - SPARC_L0_REGNUM) * 4))
+ || (!X_I (insn) && regnum == SPARC_L0_REGNUM)))
+ {
+ cache->saved_regs_mask |= (1 << (regnum - SPARC_L0_REGNUM));
+ if (X_OP3 (insn) == 0x7)
+ cache->saved_regs_mask |= (1 << (regnum + 1 - SPARC_L0_REGNUM));
+ }
+
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+ }
+
/* Recognize a SETHI insn and record its destination. */
if (X_OP (insn) == 0 && X_OP2 (insn) == 0x04)
{
dest = X_RD (insn);
offset += 4;
- insn = sparc_fetch_instruction (pc + 4);
+ insn = sparc_fetch_instruction (pc + offset);
}
/* Allow for an arithmetic operation on DEST or %g1. */
{
offset += 4;
- insn = sparc_fetch_instruction (pc + 8);
+ insn = sparc_fetch_instruction (pc + offset);
}
/* Check for the SAVE instruction that sets up the frame. */
if (X_OP (insn) == 2 && X_OP3 (insn) == 0x3c)
{
- cache->frameless_p = 0;
- return pc + offset + 4;
+ sparc_record_save_insn (cache);
+ offset += 4;
+ return pc + offset;
+ }
+
+ /* Check for an arithmetic operation on %sp. */
+ if (X_OP (insn) == 2
+ && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4)
+ && X_RS1 (insn) == SPARC_SP_REGNUM
+ && X_RD (insn) == SPARC_SP_REGNUM)
+ {
+ if (X_I (insn))
+ {
+ cache->frame_offset = X_SIMM13 (insn);
+ if (X_OP3 (insn) == 0)
+ cache->frame_offset = -cache->frame_offset;
+ }
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+
+ /* Check for an arithmetic operation that sets up the frame. */
+ if (X_OP (insn) == 2
+ && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4)
+ && X_RS1 (insn) == SPARC_SP_REGNUM
+ && X_RD (insn) == SPARC_FP_REGNUM)
+ {
+ cache->frameless_p = 0;
+ cache->frame_offset = 0;
+ /* We could check that the amount subtracted to %sp above is the
+ same as the one added here, but this seems superfluous. */
+ cache->copied_regs_mask |= 0x40;
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+ }
+
+ /* Check for a move (or) operation that copies the return register. */
+ if (X_OP (insn) == 2
+ && X_OP3 (insn) == 0x2
+ && !X_I (insn)
+ && X_RS1 (insn) == SPARC_G0_REGNUM
+ && X_RS2 (insn) == SPARC_O7_REGNUM
+ && X_RD (insn) == SPARC_I7_REGNUM)
+ {
+ cache->copied_regs_mask |= 0x80;
+ offset += 4;
+ }
+
+ return pc + offset;
}
return pc;
indeed what GCC seems to be doing. In that case GCC will
generate debug information that points to the stack slots instead
of the registers, so we should consider the instructions that
- write out these incoming arguments onto the stack. Of course we
- only need to do this if we have a stack frame. */
+ write out these incoming arguments onto the stack. */
- while (!cache.frameless_p)
+ while (1)
{
unsigned long insn = sparc_fetch_instruction (start_pc);
- /* Recognize instructions that store incoming arguments in
- %i0...%i5 into the corresponding stack slot. */
- if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04 && X_I (insn)
- && (X_RD (insn) >= 24 && X_RD (insn) <= 29) && X_RS1 (insn) == 30
- && X_SIMM13 (insn) == 68 + (X_RD (insn) - 24) * 4)
+ /* Recognize instructions that store incoming arguments into the
+ corresponding stack slots. */
+ if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04
+ && X_I (insn) && X_RS1 (insn) == SPARC_FP_REGNUM)
{
- start_pc += 4;
- continue;
+ int regnum = X_RD (insn);
+
+ /* Case of arguments still in %o[0..5]. */
+ if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O5_REGNUM
+ && !(cache.copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM)))
+ && X_SIMM13 (insn) == 68 + (regnum - SPARC_O0_REGNUM) * 4)
+ {
+ start_pc += 4;
+ continue;
+ }
+
+ /* Case of arguments copied into %i[0..5]. */
+ if (regnum >= SPARC_I0_REGNUM && regnum <= SPARC_I5_REGNUM
+ && (cache.copied_regs_mask & (1 << (regnum - SPARC_I0_REGNUM)))
+ && X_SIMM13 (insn) == 68 + (regnum - SPARC_I0_REGNUM) * 4)
+ {
+ start_pc += 4;
+ continue;
+ }
}
break;
get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM);
}
+ cache->base += cache->frame_offset;
+
if (cache->base & 1)
cache->base += BIAS;
an "unimp" instruction. If it is, then it is a struct-return
function. */
CORE_ADDR pc;
- int regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM;
+ int regnum =
+ (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM;
pc = get_frame_register_unsigned (this_frame, regnum) + 8;
if (sparc_is_unimp_insn (pc))
if (cache->struct_return_p)
pc += 4;
- regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM;
+ regnum =
+ (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM;
pc += get_frame_register_unsigned (this_frame, regnum) + 8;
return frame_unwind_got_constant (this_frame, regnum, pc);
}
}
}
- /* The previous frame's `local' and `in' registers have been saved
+ /* The previous frame's `local' and `in' registers may have been saved
in the register save area. */
- if (!cache->frameless_p
- && regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM)
+ if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM
+ && (cache->saved_regs_mask & (1 << (regnum - SPARC_L0_REGNUM))))
{
CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 4;
return frame_unwind_got_memory (this_frame, regnum, addr);
}
- /* The previous frame's `out' registers are accessible as the
- current frame's `in' registers. */
- if (!cache->frameless_p
- && regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM)
+ /* The previous frame's `out' registers may be accessible as the current
+ frame's `in' registers. */
+ if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM
+ && (cache->copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM))))
regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM);
return frame_unwind_got_register (this_frame, regnum, regnum);
{
CORE_ADDR pc = (regnum == SPARC64_NPC_REGNUM) ? 4 : 0;
- regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM;
+ regnum =
+ (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM;
pc += get_frame_register_unsigned (this_frame, regnum) + 8;
return frame_unwind_got_constant (this_frame, regnum, pc);
}
}
}
- /* The previous frame's `local' and `in' registers have been saved
+ /* The previous frame's `local' and `in' registers may have been saved
in the register save area. */
- if (!cache->frameless_p
- && regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM)
+ if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM
+ && (cache->saved_regs_mask & (1 << (regnum - SPARC_L0_REGNUM))))
{
CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 8;
return frame_unwind_got_memory (this_frame, regnum, addr);
}
- /* The previous frame's `out' registers are accessable as the
- current frame's `in' registers. */
- if (!cache->frameless_p
- && regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM)
+ /* The previous frame's `out' registers may be accessible as the current
+ frame's `in' registers. */
+ if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM
+ && (cache->copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM))))
regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM);
return frame_unwind_got_register (this_frame, regnum, regnum);