From: Andrew Cagney <cagney@gnu.org>
To: Joel Brobecker <brobecker@gnat.com>
Cc: gdb-patches@sources.redhat.com
Subject: Re: [RFA/RFC] fix problems with unwinder on mips-irix
Date: Mon, 30 Aug 2004 19:32:00 -0000 [thread overview]
Message-ID: <41338063.7020500@gnu.org> (raw)
In-Reply-To: <20040830181829.GC969@gnat.com>
> But you want a "major overhaul" of the mips unwinder as a precondition
> to fixing the problem at hand. Could you explain a bit more what
> overhaul you are interested in? I don't see what needs to be done.
>
> In particular, you said:
>
>
>>> Two key things to know:
>>>
>>> - with three unwinders handling three different cases previously handled
>>> by one, there's a lot of dead code paths. For instance,
>>> mips32_heuristic_proc_desc is now only called by mips_insn32_frame_cache
>>> and hence can be inlined there, making it possible for your problem to
>>> be solved more locally.
>
>
> mips32_heuristic_proc_desc is called by heuristic_proc_desc. (And
> frankly I find inlining often counter productive, as we end up
> with giant function just as we did with decode_line_1).
Yes, sometimes inlineing doesn't help, here it does. There's really no
value in trying to preserve this code so be brutal.
If find_proc_desc is inlined:
> static mips_extra_func_info_t
> find_proc_desc (CORE_ADDR pc, struct frame_info *next_frame, int cur_frame)
> {
> mips_extra_func_info_t proc_desc;
> CORE_ADDR startaddr = 0;
then everything from here ....
> proc_desc = non_heuristic_proc_desc (pc, &startaddr);
>
> if (proc_desc)
> {
> /* IF this is the topmost frame AND
> * (this proc does not have debugging information OR
> * the PC is in the procedure prologue)
> * THEN create a "heuristic" proc_desc (by analyzing
> * the actual code) to replace the "official" proc_desc.
> */
> if (next_frame == NULL)
> {
> struct symtab_and_line val;
> struct symbol *proc_symbol =
> PROC_DESC_IS_DUMMY (proc_desc) ? 0 : PROC_SYMBOL (proc_desc);
>
> if (proc_symbol)
> {
> val = find_pc_line (BLOCK_START
> (SYMBOL_BLOCK_VALUE (proc_symbol)), 0);
> val.pc = val.end ? val.end : pc;
> }
> if (!proc_symbol || pc < val.pc)
> {
> mips_extra_func_info_t found_heuristic =
> heuristic_proc_desc (PROC_LOW_ADDR (proc_desc),
> pc, next_frame, cur_frame);
> if (found_heuristic)
> proc_desc = found_heuristic;
> }
> }
> }
> else
> {
> /* Is linked_proc_desc_table really necessary? It only seems to be used
> by procedure call dummys. However, the procedures being called ought
> to have their own proc_descs, and even if they don't,
> heuristic_proc_desc knows how to create them! */
>
> struct linked_proc_info *link;
>
> for (link = linked_proc_desc_table; link; link = link->next)
> if (PROC_LOW_ADDR (&link->info) <= pc
> && PROC_HIGH_ADDR (&link->info) > pc)
> return &link->info;
... to here is simply dead. That is all for the non-heuristic case.
That leaves:
> if (startaddr == 0)
> startaddr = heuristic_proc_start (pc);
>
> proc_desc = heuristic_proc_desc (startaddr, pc, next_frame, cur_frame);
> }
> return proc_desc;
> }
If heuristic_proc_descr is then inlined we find:
> static mips_extra_func_info_t
> heuristic_proc_desc (CORE_ADDR start_pc, CORE_ADDR limit_pc,
> struct frame_info *next_frame, int cur_frame)
> {
> CORE_ADDR sp;
>
> if (cur_frame)
> sp = read_next_frame_reg (next_frame, NUM_REGS + MIPS_SP_REGNUM);
> else
> sp = 0;
>
> if (start_pc == 0)
> return NULL;
> memset (&temp_proc_desc, '\0', sizeof (temp_proc_desc));
> temp_saved_regs = xrealloc (temp_saved_regs, SIZEOF_FRAME_SAVED_REGS);
> memset (temp_saved_regs, '\0', SIZEOF_FRAME_SAVED_REGS);
> PROC_LOW_ADDR (&temp_proc_desc) = start_pc;
> PROC_FRAME_REG (&temp_proc_desc) = MIPS_SP_REGNUM;
> PROC_PC_REG (&temp_proc_desc) = RA_REGNUM;
> if (start_pc + 200 < limit_pc)
> limit_pc = start_pc + 200;
> if (pc_is_mips16 (start_pc))
this mips16 call is dead.
> mips16_heuristic_proc_desc (start_pc, limit_pc, next_frame, sp);
> else
> mips32_heuristic_proc_desc (start_pc, limit_pc, next_frame, sp);
> return &temp_proc_desc;
> }
If we then inline the mips32_heuristic ... call into mips_insn32...:
> static void
> mips32_heuristic_proc_desc (CORE_ADDR start_pc, CORE_ADDR limit_pc,
> struct frame_info *next_frame, CORE_ADDR sp)
> {
> CORE_ADDR cur_pc;
> CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for frame-pointer */
> restart:
> temp_saved_regs = xrealloc (temp_saved_regs, SIZEOF_FRAME_SAVED_REGS);
> memset (temp_saved_regs, '\0', SIZEOF_FRAME_SAVED_REGS);
> PROC_FRAME_OFFSET (&temp_proc_desc) = 0;
> PROC_FRAME_ADJUST (&temp_proc_desc) = 0; /* offset of FP from SP */
> for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSTLEN)
> {
> unsigned long inst, high_word, low_word;
> int reg;
>
> /* Fetch the instruction. */
> inst = (unsigned long) mips_fetch_instruction (cur_pc);
>
> /* Save some code by pre-extracting some useful fields. */
> high_word = (inst >> 16) & 0xffff;
> low_word = inst & 0xffff;
> reg = high_word & 0x1f;
>
> if (high_word == 0x27bd /* addiu $sp,$sp,-i */
> || high_word == 0x23bd /* addi $sp,$sp,-i */
> || high_word == 0x67bd) /* daddiu $sp,$sp,-i */
> {
> if (low_word & 0x8000) /* negative stack adjustment? */
> PROC_FRAME_OFFSET (&temp_proc_desc) += 0x10000 - low_word;
> else
> /* Exit loop if a positive stack adjustment is found, which
> usually means that the stack cleanup code in the function
> epilogue is reached. */
> break;
> }
> else if ((high_word & 0xFFE0) == 0xafa0) /* sw reg,offset($sp) */
> {
> PROC_REG_MASK (&temp_proc_desc) |= 1 << reg;
> set_reg_offset (temp_saved_regs, reg, sp + low_word);
> }
> else if ((high_word & 0xFFE0) == 0xffa0) /* sd reg,offset($sp) */
> {
> /* Irix 6.2 N32 ABI uses sd instructions for saving $gp and
> $ra. */
> PROC_REG_MASK (&temp_proc_desc) |= 1 << reg;
> set_reg_offset (temp_saved_regs, reg, sp + low_word);
> }
> else if (high_word == 0x27be) /* addiu $30,$sp,size */
> {
> /* Old gcc frame, r30 is virtual frame pointer. */
> if ((long) low_word != PROC_FRAME_OFFSET (&temp_proc_desc))
> frame_addr = sp + low_word;
> else if (PROC_FRAME_REG (&temp_proc_desc) == MIPS_SP_REGNUM)
> {
> unsigned alloca_adjust;
> PROC_FRAME_REG (&temp_proc_desc) = 30;
> frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30);
> alloca_adjust = (unsigned) (frame_addr - (sp + low_word));
> if (alloca_adjust > 0)
> {
> /* FP > SP + frame_size. This may be because
> * of an alloca or somethings similar.
> * Fix sp to "pre-alloca" value, and try again.
> */
> sp += alloca_adjust;
> goto restart;
> }
> }
> }
> /* move $30,$sp. With different versions of gas this will be either
> `addu $30,$sp,$zero' or `or $30,$sp,$zero' or `daddu 30,sp,$0'.
> Accept any one of these. */
> else if (inst == 0x03A0F021 || inst == 0x03a0f025 || inst == 0x03a0f02d)
> {
> /* New gcc frame, virtual frame pointer is at r30 + frame_size. */
> if (PROC_FRAME_REG (&temp_proc_desc) == MIPS_SP_REGNUM)
> {
> unsigned alloca_adjust;
> PROC_FRAME_REG (&temp_proc_desc) = 30;
> frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30);
> alloca_adjust = (unsigned) (frame_addr - sp);
> if (alloca_adjust > 0)
> {
> /* FP > SP + frame_size. This may be because
> * of an alloca or somethings similar.
> * Fix sp to "pre-alloca" value, and try again.
> */
> sp += alloca_adjust;
> goto restart;
> }
> }
> }
> else if ((high_word & 0xFFE0) == 0xafc0) /* sw reg,offset($30) */
> {
> PROC_REG_MASK (&temp_proc_desc) |= 1 << reg;
> set_reg_offset (temp_saved_regs, reg, frame_addr + low_word);
> }
> }
> }
>
and compare it against these parts of the body of mips_insn32's
heuristic ..., we also find:
> static struct mips_frame_cache *
> mips_insn32_frame_cache (struct frame_info *next_frame, void **this_cache)
> {
> mips_extra_func_info_t proc_desc;
> struct mips_frame_cache *cache;
> struct gdbarch *gdbarch = get_frame_arch (next_frame);
> struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> /* r0 bit means kernel trap */
> int kernel_trap;
> /* What registers have been saved? Bitmasks. */
> unsigned long gen_mask, float_mask;
>
> if ((*this_cache) != NULL)
> return (*this_cache);
> cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache);
> (*this_cache) = cache;
> cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
this is has been inlined down to mips32_...
> /* Get the mdebug proc descriptor. */
> proc_desc = find_proc_desc (frame_pc_unwind (next_frame), next_frame, 1);
> if (proc_desc == NULL)
> /* I'm not sure how/whether this can happen. Normally when we
> can't find a proc_desc, we "synthesize" one using
> heuristic_proc_desc and set the saved_regs right away. */
> return cache;
>
> /* Extract the frame's base. */
> cache->base = (frame_unwind_register_signed (next_frame, NUM_REGS + PROC_FRAME_REG (proc_desc))
> + PROC_FRAME_OFFSET (proc_desc) - PROC_FRAME_ADJUST (proc_desc));
This kernel_trap stuff isn't applicable here.
> kernel_trap = PROC_REG_MASK (proc_desc) & 1;
> gen_mask = kernel_trap ? 0xFFFFFFFF : PROC_REG_MASK (proc_desc);
> float_mask = kernel_trap ? 0xFFFFFFFF : PROC_FREG_MASK (proc_desc);
THe following is just duplicating that inlined mips32... so the pair can
be folded into one.
> /* In any frame other than the innermost or a frame interrupted by a
> signal, we assume that all registers have been saved. This
> assumes that all register saves in a function happen before the
> first function call. */
> if (in_prologue (frame_pc_unwind (next_frame), PROC_LOW_ADDR (proc_desc))
> /* Not sure exactly what kernel_trap means, but if it means the
> kernel saves the registers without a prologue doing it, we
> better not examine the prologue to see whether registers
> have been saved yet. */
> && !kernel_trap)
> {
> /* We need to figure out whether the registers that the
> proc_desc claims are saved have been saved yet. */
>
> CORE_ADDR addr;
>
> /* Bitmasks; set if we have found a save for the register. */
> unsigned long gen_save_found = 0;
> unsigned long float_save_found = 0;
> int mips16;
>
> /* If the address is odd, assume this is MIPS16 code. */
> addr = PROC_LOW_ADDR (proc_desc);
> mips16 = pc_is_mips16 (addr);
>
> /* Scan through this function's instructions preceding the
> current PC, and look for those that save registers. */
> while (addr < frame_pc_unwind (next_frame))
> {
> if (mips16)
> {
> mips16_decode_reg_save (mips16_fetch_instruction (addr),
> &gen_save_found);
> addr += MIPS16_INSTLEN;
> }
> else
> {
> mips32_decode_reg_save (mips32_fetch_instruction (addr),
> &gen_save_found, &float_save_found);
> addr += MIPS_INSTLEN;
> }
> }
> gen_mask = gen_save_found;
> float_mask = float_save_found;
> }
>
> /* Fill in the offsets for the registers which gen_mask says were
> saved. */
> {
> CORE_ADDR reg_position = (cache->base
> + PROC_REG_OFFSET (proc_desc));
> int ireg;
> for (ireg = MIPS_NUMREGS - 1; gen_mask; --ireg, gen_mask <<= 1)
> if (gen_mask & 0x80000000)
> {
> cache->saved_regs[NUM_REGS + ireg].addr = reg_position;
> reg_position -= mips_abi_regsize (gdbarch);
> }
> }
this is dead ...
> /* The MIPS16 entry instruction saves $s0 and $s1 in the reverse
> order of that normally used by gcc. Therefore, we have to fetch
> the first instruction of the function, and if it's an entry
> instruction that saves $s0 or $s1, correct their saved addresses. */
> if (pc_is_mips16 (PROC_LOW_ADDR (proc_desc)))
> {
> ULONGEST inst = mips16_fetch_instruction (PROC_LOW_ADDR (proc_desc));
> if ((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700)
> /* entry */
> {
> int reg;
> int sreg_count = (inst >> 6) & 3;
>
> /* Check if the ra register was pushed on the stack. */
> CORE_ADDR reg_position = (cache->base
> + PROC_REG_OFFSET (proc_desc));
> if (inst & 0x20)
> reg_position -= mips_abi_regsize (gdbarch);
>
> /* Check if the s0 and s1 registers were pushed on the
> stack. */
> /* NOTE: cagney/2004-02-08: Huh? This is doing no such
> check. */
> for (reg = 16; reg < sreg_count + 16; reg++)
> {
> cache->saved_regs[NUM_REGS + reg].addr = reg_position;
> reg_position -= mips_abi_regsize (gdbarch);
> }
> }
> }
this is redundant (We've got the same loop (or equivalent)) in the
mips32_heuristic_proc_desc.
> /* Fill in the offsets for the registers which float_mask says were
> saved. */
> {
> CORE_ADDR reg_position = (cache->base
> + PROC_FREG_OFFSET (proc_desc));
> int ireg;
> /* Fill in the offsets for the float registers which float_mask
> says were saved. */
> for (ireg = MIPS_NUMREGS - 1; float_mask; --ireg, float_mask <<= 1)
> if (float_mask & 0x80000000)
> {
> if (mips_abi_regsize (gdbarch) == 4
> && TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
> {
> /* On a big endian 32 bit ABI, floating point registers
> are paired to form doubles such that the most
> significant part is in $f[N+1] and the least
> significant in $f[N] vis: $f[N+1] ||| $f[N]. The
> registers are also spilled as a pair and stored as a
> double.
>
> When little-endian the least significant part is
> stored first leading to the memory order $f[N] and
> then $f[N+1].
>
> Unfortunately, when big-endian the most significant
> part of the double is stored first, and the least
> significant is stored second. This leads to the
> registers being ordered in memory as firt $f[N+1] and
> then $f[N].
>
> For the big-endian case make certain that the
> addresses point at the correct (swapped) locations
> $f[N] and $f[N+1] pair (keep in mind that
> reg_position is decremented each time through the
> loop). */
> if ((ireg & 1))
> cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
> .addr = reg_position - mips_abi_regsize (gdbarch);
> else
> cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
> .addr = reg_position + mips_abi_regsize (gdbarch);
> }
> else
> cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
> .addr = reg_position;
> reg_position -= mips_abi_regsize (gdbarch);
> }
>
> cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc]
> = cache->saved_regs[NUM_REGS + RA_REGNUM];
> }
>
> /* SP_REGNUM, contains the value and not the address. */
> trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base);
>
> return (*this_cache);
> }
and so it goes
have fun!
Andrew
next prev parent reply other threads:[~2004-08-30 19:32 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-07-23 1:11 Joel Brobecker
2004-07-30 0:22 ` Andrew Cagney
2004-08-03 4:44 ` Joel Brobecker
2004-08-04 1:43 ` Andrew Cagney
2004-08-06 18:31 ` Joel Brobecker
2004-08-06 19:13 ` Andrew Cagney
2004-08-30 18:18 ` Joel Brobecker
2004-08-30 19:32 ` Andrew Cagney [this message]
2004-08-31 23:44 ` Joel Brobecker
2004-09-01 14:57 ` Andrew Cagney
2004-09-02 23:09 ` Joel Brobecker
2004-09-03 20:17 ` Andrew Cagney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=41338063.7020500@gnu.org \
--to=cagney@gnu.org \
--cc=brobecker@gnat.com \
--cc=gdb-patches@sources.redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox