This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[committed] MIPS: Correct heuristic prologue termination conditions


Hi,

 In testing an upcoming change I noticed a regression in 
gdb.dwarf2/dw2-skip-prologue.exp across MIPS16 multilibs:

(gdb) file .../gdb.dwarf2/dw2-skip-prologue
Reading symbols from .../gdb.d/gdb.dwarf2/dw2-skip-prologue...done.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break main
warning: Breakpoint address adjusted from 0x00400725 to 0x00400721.
Breakpoint 1 at 0x400721
(gdb) set remotetimeout 5
(gdb) kill
The program is not being run.
(gdb) 
[...]
target remote ...:2345
Reading symbols from .../mips16/lib/ld.so.1...done.
warning: Breakpoint address adjusted from 0x00400725 to 0x00400721.
warning: Breakpoint address adjusted from 0x00400725 to 0x00400721.
0x2aaa8e81 in __start () from .../mips16/lib/ld.so.1
(gdb) continue
Continuing.
warning: Breakpoint address adjusted from 0x00400725 to 0x00400721.
warning: Breakpoint 1 address previously adjusted from 0x00400725 to 
0x00400721.
Breakpoint 1, 0x00400721 in main ()
(gdb) break func
Breakpoint 2 at 0x4006a1: func. (2 locations)
(gdb) continue
Continuing.
warning: GDB can't find the start of the function at 0x4006dd.

    GDB is unable to find the start of the function at 0x4006dd
and thus can't determine the size of that function's stack frame.
This means that GDB may be unable to access that stack frame, or
the frames below it.
    This problem is most likely caused by an invalid program counter or
stack pointer.
    However, if you think GDB should simply search farther back
from 0x4006dd for code which looks like the beginning of a
function, you can increase the range of the search using the `set
heuristic-fence-post' command.

Program received signal SIGBUS, Bus error.
0x0040072b in main ()
(gdb) FAIL: gdb.dwarf2/dw2-skip-prologue.exp: continue to breakpoint: func

-- notice the breakpoint adjustment messages that are already a bad sign.  
These happen when a breakpoint is requested in a branch delay slot and are 
not supposed to happen unless explicitly requested with an address 
pointing to a branch delay slot instruction.  No symbol or line debug 
information is supposed to direct GDB to place a breakpoint in a delay 
slot.

 Here's how `main' looks like:

00400718 <main>:
  400718:	64f5      	save    40,ra,s0-s1
  40071a:	1a00 01a8 	jal	4006a0 <func>
  40071e:	0104      	addiu	s1,sp,16
  400720:	1a00 01b7 	jal	4006dc <func+0x3c>
  400724:	6702      	move	s0,v0
  400726:	e049      	addu	v0,s0,v0
  400728:	65b9      	move	sp,s1
  40072a:	6473      	restore	24,ra,s0-s1
  40072c:	e8a0      	jrc	ra
  40072e:	6500      	nop

-- so 0x400725 is the MIPS16 instruction address of the first MOVE 
instruction seen above, in a delay slot of the preceding JAL instruction 
indeed.  This test case arranges for `main' to have no debug information 
so it is one of the heuristic prologue scanners, `mips16_scan_prologue' 
specifically in this case, that is responsible for finding the right 
location for the breakpoint to place.

 In this case the prologue really ends with the ADDIU instruction, 
reordered into the delay slot of the first JAL instruction.  Of course 
we can't place the breakpoint for `main' after it as by doing so we'll 
let `func' to be called before hitting this breakpoint.  So the breakpoint 
has to go at the JAL instruction instead, or 0x40071b.

 To make a general case out of it we must never consider any jump or 
branch instruction to be a part of a function's prologue.  In the presence 
of a jump or branch at the beginning of a function the furthest 
instruction examined for the purpose of constructing frame information can 
be one in the delay slot of that jump or branch if present, and otherwise 
-- that is when the jump or branch is compact and has no delay slot -- the 
instruction immediately preceding the jump or branch.

 This change implements that approach across prologue scanners for the 
three instruction ISAs.  In implementing it I have factored out code from 
the existing `*_instruction_has_delay_slot' handlers to be shared and a 
side effect for the microMIPS implementation is it now always fetches the 
second 16-bit halfword of 32-bit instructions even if it eventually is not 
going to be needed.  I think it's an acceptable tradeoff for the purpose 
of code sharing.

 To make things more consistent I also carried logic from 
`micromips_scan_prologue' over to the other two scanners to accept (and 
ignore) a single non-prologue non-control transfer instruction reordered 
by the compiler into the prologue.  While doing this I simplified the exit 
path from the scan loop such that `end_prologue_addr' is set only once.  
This made some concerns expressed in comments no longer applicable, 
although even before they were not valid.

 I have not fixed the logic around `load_immediate_bytes' in 
`mips32_scan_prologue' though, it remains broken, although I took care not 
to break it more.  An approach similar to one taken for handling larger 
stack adjustments in `micromips_scan_prologue' will have to be eventually 
implemented here.

 For regression testing I used my usual choice of the mips-linux-gnu 
target and the following multilibs:

-EB
-EB -msoft-float
-EB -mips16
-EB -mips16 -msoft-float
-EB -mmicromips
-EB -mmicromips -msoft-float
-EB -mabi=n32
-EB -mabi=n32 -msoft-float
-EB -mabi=64
-EB -mabi=64 -msoft-float

and the -EL variants of same.

 That removed gdb.dwarf2/dw2-skip-prologue.exp failures across MIPS16 
multilibs, the test log now shows:

(gdb) file .../gdb.dwarf2/dw2-skip-prologue
Reading symbols from .../gdb.d/gdb.dwarf2/dw2-skip-prologue...done.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break main
Breakpoint 1 at 0x40071b
(gdb) set remotetimeout 5
(gdb) kill
The program is not being run.
(gdb) 
[...]
target remote ...:2345
Reading symbols from .../mips16/lib/ld.so.1...done.
0x2aaa8e81 in __start () from .../mips16/lib/ld.so.1
(gdb) continue
Continuing.

Breakpoint 1, 0x0040071b in main ()
(gdb) break func
Breakpoint 2 at 0x4006a1: func. (2 locations)
(gdb) continue
Continuing.

Breakpoint 2, func (param=0) at main.c:5
5	   This program is free software; you can redistribute it and/or modify
(gdb) PASS: gdb.dwarf2/dw2-skip-prologue.exp: continue to breakpoint: func

-- so things look like intended.

 That also did regress, again across MIPS16 multilibs, another test case, 
gdb.base/step-symless.exp:

(gdb) file .../gdb.d/gdb.base/step-symless
Reading symbols from .../gdb.base/step-symless...done.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break main
Breakpoint 1 at 0x4006d3
(gdb) set remotetimeout 5
(gdb) kill
The program is not being run.
(gdb) 
[...]
target remote ...:2345
Reading symbols from .../mips16/lib/ld.so.1...done.
0x2aaa8e81 in __start () from .../mips16/lib/ld.so.1
(gdb) continue
Continuing.

Breakpoint 1, 0x004006d3 in main ()
(gdb) break symful
Breakpoint 2 at 0x4006a5
(gdb) step
Single stepping until exit from function main,
which has no line number information.
warning: GDB can't find the start of the function at 0x4006b9.

    GDB is unable to find the start of the function at 0x4006b9
and thus can't determine the size of that function's stack frame.
This means that GDB may be unable to access that stack frame, or
the frames below it.
    This problem is most likely caused by an invalid program counter or
stack pointer.
    However, if you think GDB should simply search farther back
from 0x4006b9 for code which looks like the beginning of a
function, you can increase the range of the search using the `set
heuristic-fence-post' command.
0x004006b9 in ?? ()
(gdb) FAIL: gdb.base/step-symless.exp: step

-- but that is actually a good sign.  Here `main', again, has no debug 
information and code involved looks like:

004006a0 <symful>:
  4006a0:	6491      	save	8,s1
  4006a2:	673d      	move	s1,sp
  4006a4:	b204      	lw	v0,4006b4 <symful+0x14>
  4006a6:	9a40      	lw	v0,0(v0)
  4006a8:	4261      	addiu	v1,v0,1
  4006aa:	b203      	lw	v0,4006b4 <symful+0x14>
  4006ac:	da60      	sw	v1,0(v0)
  4006ae:	65b9      	move	sp,s1
  4006b0:	6411      	restore	8,s1
  4006b2:	e8a0      	jrc	ra
  4006b4:	0041      	addiu	s0,sp,260
  4006b6:	0860      	la	s0,400834 <__libc_start_main@mips16plt+0x54>
  4006b8:	6491      	save	8,s1
  4006ba:	673d      	move	s1,sp
  4006bc:	b204      	lw	v0,4006cc <symful+0x2c>
  4006be:	9a40      	lw	v0,0(v0)
  4006c0:	4261      	addiu	v1,v0,1
  4006c2:	b203      	lw	v0,4006cc <symful+0x2c>
  4006c4:	da60      	sw	v1,0(v0)
  4006c6:	65b9      	move	sp,s1
  4006c8:	6411      	restore	8,s1
  4006ca:	e8a0      	jrc	ra
  4006cc:	0041      	addiu	s0,sp,260
  4006ce:	0860      	la	s0,40084c <__libc_start_main@mips16plt+0x6c>

004006d0 <main>:
  4006d0:	64d4      	save	32,ra,s1
  4006d2:	1a00 01ae 	jal	4006b8 <symful+0x18>
  4006d6:	0104      	addiu	s1,sp,16
  4006d8:	1a00 01a8 	jal	4006a0 <symful>
  4006dc:	6500      	nop
  4006de:	6740      	move	v0,zero
  4006e0:	65b9      	move	sp,s1
  4006e2:	6452      	restore	16,ra,s1
  4006e4:	e8a0      	jrc	ra
  4006e6:	6500      	nop
  4006e8:	6500      	nop
  4006ea:	6500      	nop
  4006ec:	6500      	nop
  4006ee:	6500      	nop

-- and the original log:

(gdb) file .../gdb.base/step-symless
Reading symbols from .../gdb.base/step-symless...done.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break main
warning: Breakpoint address adjusted from 0x004006dd to 0x004006d9.
Breakpoint 1 at 0x4006d9
(gdb) set remotetimeout 5
(gdb) kill
The program is not being run.
(gdb) 
[...]
target remote ...:2345
Reading symbols from .../mips16/lib/ld.so.1...done.
warning: Breakpoint address adjusted from 0x004006dd to 0x004006d9.
warning: Breakpoint address adjusted from 0x004006dd to 0x004006d9.
0x2aaa8e81 in __start () from .../mips16/lib/ld.so.1
(gdb) continue
Continuing.
warning: Breakpoint address adjusted from 0x004006dd to 0x004006d9.
warning: Breakpoint 1 address previously adjusted from 0x004006dd to 
0x004006d9.
Breakpoint 1, 0x004006d9 in main ()
(gdb) break symful
Breakpoint 2 at 0x4006a5
(gdb) step
Single stepping until exit from function main,
which has no line number information.

Breakpoint 2, 0x004006a5 in symful ()
(gdb) PASS: gdb.base/step-symless.exp: step

So the breakpoint at `main' was actually set at an instruction after the 
call to `symful+0x18' aka `symless' and the test only passed because 
single-stepping through `symless' wasn't actually done at all.  With this 
change in place this test fails for MIPS16 multilibs consistently with all 
the other multilibs where it already failed in this manner previously.

 I have committed this change now.

2014-10-05  Maciej W. Rozycki  <macro@codesourcery.com>

	gdb/
	* mips-tdep.c (mips16_instruction_is_compact_branch): New
	function.
	(micromips_instruction_is_compact_branch): Likewise.
	(mips16_scan_prologue): Terminate scanning upon seeing a branch
	or a compact jump, reaching a jump delay slot, or seeing a
	second non-prologue instruction.
	(micromips_scan_prologue): Also terminate scanning upon seeing a
	compact branch or jump, or reaching a branch or jump delay slot.
	(mips32_scan_prologue): Terminate scanning upon reaching a branch
	or jump delay slot, or seeing a second non-prologue instruction.
	(mips32_instruction_has_delay_slot): Retain instruction
	examination code only, update arguments accordingly and move
	instruction fetch pieces to...
	(mips32_insn_at_pc_has_delay_slot): ... this new function.
	(micromips_instruction_has_delay_slot): Likewise and to...
	(micromips_insn_at_pc_has_delay_slot): ... this new function.
	(mips16_instruction_has_delay_slot): Likewise and to...
	(mips16_insn_at_pc_has_delay_slot): ... this new function.
	(mips_single_step_through_delay): Update accordingly.
	(mips_adjust_breakpoint_address): Likewise.

  Maciej

gdb-mips-scan-prologue.diff
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2014-10-05 23:03:19.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2014-10-05 23:04:46.598139355 +0100
@@ -60,11 +60,18 @@ static const struct objfile_data *mips_p
 
 static struct type *mips_register_type (struct gdbarch *gdbarch, int regnum);
 
-static int mips32_instruction_has_delay_slot (struct gdbarch *, CORE_ADDR);
-static int micromips_instruction_has_delay_slot (struct gdbarch *, CORE_ADDR,
-						 int);
-static int mips16_instruction_has_delay_slot (struct gdbarch *, CORE_ADDR,
-					      int);
+static int mips32_instruction_has_delay_slot (struct gdbarch *gdbarch,
+					      ULONGEST inst);
+static int micromips_instruction_has_delay_slot (ULONGEST insn, int mustbe32);
+static int mips16_instruction_has_delay_slot (unsigned short inst,
+					      int mustbe32);
+
+static int mips32_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
+					     CORE_ADDR addr);
+static int micromips_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
+						CORE_ADDR addr, int mustbe32);
+static int mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
+					     CORE_ADDR addr, int mustbe32);
 
 /* A useful bit in the CP0 status register (MIPS_PS_REGNUM).  */
 /* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip.  */
@@ -2307,6 +2314,48 @@ mips_next_pc (struct frame_info *frame, 
     return mips32_next_pc (frame, pc);
 }
 
+/* Return non-zero if the MIPS16 instruction INSN is a compact branch
+   or jump.  */
+
+static int
+mips16_instruction_is_compact_branch (unsigned short insn)
+{
+  switch (insn & 0xf800)
+    {
+    case 0xe800:
+      return (insn & 0x009f) == 0x80;	/* JALRC/JRC */
+    case 0x6000:
+      return (insn & 0x0600) == 0;	/* BTNEZ/BTEQZ */
+    case 0x2800:			/* BNEZ */
+    case 0x2000:			/* BEQZ */
+    case 0x1000:			/* B */
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Return non-zero if the microMIPS instruction INSN is a compact branch
+   or jump.  */
+
+static int
+micromips_instruction_is_compact_branch (unsigned short insn)
+{
+  switch (micromips_op (insn))
+    {
+    case 0x11:			/* POOL16C: bits 010001 */
+      return (b5s5_op (insn) == 0x18
+				/* JRADDIUSP: bits 010001 11000 */
+	      || b5s5_op (insn) == 0xd);
+				/* JRC: bits 010011 01101 */
+    case 0x10:			/* POOL32I: bits 010000 */
+      return (b5s5_op (insn) & 0x1d) == 0x5;
+				/* BEQZC/BNEZC: bits 010000 001x1 */
+    default:
+      return 0;
+    }
+}
+
 struct mips_frame_cache
 {
   CORE_ADDR base;
@@ -2384,6 +2433,10 @@ mips16_scan_prologue (struct gdbarch *gd
                       struct frame_info *this_frame,
                       struct mips_frame_cache *this_cache)
 {
+  int prev_non_prologue_insn = 0;
+  int this_non_prologue_insn;
+  int non_prologue_insns = 0;
+  CORE_ADDR prev_pc;
   CORE_ADDR cur_pc;
   CORE_ADDR frame_addr = 0;	/* Value of $r17, used as frame pointer.  */
   CORE_ADDR sp;
@@ -2394,11 +2447,13 @@ mips16_scan_prologue (struct gdbarch *gd
   unsigned inst = 0;		/* current instruction */
   unsigned entry_inst = 0;	/* the entry instruction */
   unsigned save_inst = 0;	/* the save instruction */
+  int prev_delay_slot = 0;
+  int in_delay_slot;
   int reg, offset;
 
   int extend_bytes = 0;
-  int prev_extend_bytes;
-  CORE_ADDR end_prologue_addr = 0;
+  int prev_extend_bytes = 0;
+  CORE_ADDR end_prologue_addr;
 
   /* Can be called when there's no process, and hence when there's no
      THIS_FRAME.  */
@@ -2411,9 +2466,16 @@ mips16_scan_prologue (struct gdbarch *gd
 
   if (limit_pc > start_pc + 200)
     limit_pc = start_pc + 200;
+  prev_pc = start_pc;
 
+  /* Permit at most one non-prologue non-control-transfer instruction
+     in the middle which may have been reordered by the compiler for
+     optimisation.  */
   for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSN16_SIZE)
     {
+      this_non_prologue_insn = 0;
+      in_delay_slot = 0;
+
       /* Save the previous instruction.  If it's an EXTEND, we'll extract
          the immediate offset extension from it in mips16_get_imm.  */
       prev_inst = inst;
@@ -2503,21 +2565,40 @@ mips16_scan_prologue (struct gdbarch *gd
 	  if (prev_extend_bytes)		/* extend */
 	    save_inst |= prev_inst << 16;
 	}
-      else if ((inst & 0xf800) == 0x1800)	/* jal(x) */
-	cur_pc += MIPS_INSN16_SIZE;	/* 32-bit instruction */
       else if ((inst & 0xff1c) == 0x6704)	/* move reg,$a0-$a3 */
         {
           /* This instruction is part of the prologue, but we don't
              need to do anything special to handle it.  */
         }
+      else if (mips16_instruction_has_delay_slot (inst, 0))
+						/* JAL/JALR/JALX/JR */
+	{
+	  /* The instruction in the delay slot can be a part
+	     of the prologue, so move forward once more.  */
+	  in_delay_slot = 1;
+	  if (mips16_instruction_has_delay_slot (inst, 1))
+						/* JAL/JALX */
+	    {
+	      prev_extend_bytes = MIPS_INSN16_SIZE;
+	      cur_pc += MIPS_INSN16_SIZE;	/* 32-bit instruction */
+	    }
+	}
       else
         {
-          /* This instruction is not an instruction typically found
-             in a prologue, so we must have reached the end of the
-             prologue.  */
-          if (end_prologue_addr == 0)
-            end_prologue_addr = cur_pc - prev_extend_bytes;
+	  this_non_prologue_insn = 1;
         }
+
+      non_prologue_insns += this_non_prologue_insn;
+
+      /* A jump or branch, or enough non-prologue insns seen?  If so,
+         then we must have reached the end of the prologue by now.  */
+      if (prev_delay_slot || non_prologue_insns > 1
+	  || mips16_instruction_is_compact_branch (inst))
+	break;
+
+      prev_non_prologue_insn = this_non_prologue_insn;
+      prev_delay_slot = in_delay_slot;
+      prev_pc = cur_pc - prev_extend_bytes;
     }
 
   /* The entry instruction is typically the first instruction in a function,
@@ -2670,11 +2751,12 @@ mips16_scan_prologue (struct gdbarch *gd
         = this_cache->saved_regs[gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM];
     }
 
-  /* If we didn't reach the end of the prologue when scanning the function
-     instructions, then set end_prologue_addr to the address of the
-     instruction immediately after the last one we scanned.  */
-  if (end_prologue_addr == 0)
-    end_prologue_addr = cur_pc;
+  /* Set end_prologue_addr to the address of the instruction immediately
+     after the last one we scanned.  Unless the last one looked like a
+     non-prologue instruction (and we looked ahead), in which case use
+     its address instead.  */
+  end_prologue_addr = (prev_non_prologue_insn || prev_delay_slot
+		       ? prev_pc : cur_pc - prev_extend_bytes);
 
   return end_prologue_addr;
 }
@@ -2811,7 +2893,7 @@ micromips_scan_prologue (struct gdbarch 
 			 struct frame_info *this_frame,
 			 struct mips_frame_cache *this_cache)
 {
-  CORE_ADDR end_prologue_addr = 0;
+  CORE_ADDR end_prologue_addr;
   int prev_non_prologue_insn = 0;
   int frame_reg = MIPS_SP_REGNUM;
   int this_non_prologue_insn;
@@ -2819,6 +2901,8 @@ micromips_scan_prologue (struct gdbarch 
   long frame_offset = 0;	/* Size of stack frame.  */
   long frame_adjust = 0;	/* Offset of FP from SP.  */
   CORE_ADDR frame_addr = 0;	/* Value of $30, used as frame pointer.  */
+  int prev_delay_slot = 0;
+  int in_delay_slot;
   CORE_ADDR prev_pc;
   CORE_ADDR cur_pc;
   ULONGEST insn;		/* current instruction */
@@ -2855,6 +2939,7 @@ micromips_scan_prologue (struct gdbarch 
   for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += loc)
     {
       this_non_prologue_insn = 0;
+      in_delay_slot = 0;
       sp_adj = 0;
       loc = 0;
       insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, cur_pc, NULL);
@@ -3007,9 +3092,15 @@ micromips_scan_prologue (struct gdbarch 
 	      break;
 
 	    default:
-	      this_non_prologue_insn = 1;
+	      /* The instruction in the delay slot can be a part
+	         of the prologue, so move forward once more.  */
+	      if (micromips_instruction_has_delay_slot (insn, 0))
+		in_delay_slot = 1;
+	      else
+		this_non_prologue_insn = 1;
 	      break;
 	    }
+	  insn >>= 16;
 	  break;
 
 	/* 16-bit instructions.  */
@@ -3064,7 +3155,12 @@ micromips_scan_prologue (struct gdbarch 
 	      break;
 
 	    default:
-	      this_non_prologue_insn = 1;
+	      /* The instruction in the delay slot can be a part
+	         of the prologue, so move forward once more.  */
+	      if (micromips_instruction_has_delay_slot (insn << 16, 0))
+		in_delay_slot = 1;
+	      else
+		this_non_prologue_insn = 1;
 	      break;
 	    }
 	  break;
@@ -3073,13 +3169,16 @@ micromips_scan_prologue (struct gdbarch 
 	frame_offset -= sp_adj;
 
       non_prologue_insns += this_non_prologue_insn;
-      /* Enough non-prologue insns seen or positive stack adjustment? */
-      if (end_prologue_addr == 0 && (non_prologue_insns > 1 || sp_adj > 0))
-	{
-	  end_prologue_addr = prev_non_prologue_insn ? prev_pc : cur_pc;
-	  break;
-	}
+
+      /* A jump or branch, enough non-prologue insns seen or positive
+         stack adjustment?  If so, then we must have reached the end
+         of the prologue by now.  */
+      if (prev_delay_slot || non_prologue_insns > 1 || sp_adj > 0
+	  || micromips_instruction_is_compact_branch (insn))
+	break;
+
       prev_non_prologue_insn = this_non_prologue_insn;
+      prev_delay_slot = in_delay_slot;
       prev_pc = cur_pc;
     }
 
@@ -3097,13 +3196,12 @@ micromips_scan_prologue (struct gdbarch 
 	= this_cache->saved_regs[gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM];
     }
 
-  /* If we didn't reach the end of the prologue when scanning the function
-     instructions, then set end_prologue_addr to the address of the
-     instruction immediately after the last one we scanned.  Unless the
-     last one looked like a non-prologue instruction (and we looked ahead),
-     in which case use its address instead.  */
-  if (end_prologue_addr == 0)
-    end_prologue_addr = prev_non_prologue_insn ? prev_pc : cur_pc;
+  /* Set end_prologue_addr to the address of the instruction immediately
+     after the last one we scanned.  Unless the last one looked like a
+     non-prologue instruction (and we looked ahead), in which case use
+     its address instead.  */
+  end_prologue_addr
+    = prev_non_prologue_insn || prev_delay_slot ? prev_pc : cur_pc;
 
   return end_prologue_addr;
 }
@@ -3251,17 +3349,22 @@ mips32_scan_prologue (struct gdbarch *gd
                       struct frame_info *this_frame,
                       struct mips_frame_cache *this_cache)
 {
-  CORE_ADDR cur_pc;
+  int prev_non_prologue_insn;
+  int this_non_prologue_insn;
+  int non_prologue_insns;
   CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for
 			       frame-pointer.  */
+  int prev_delay_slot;
+  CORE_ADDR prev_pc;
+  CORE_ADDR cur_pc;
   CORE_ADDR sp;
   long frame_offset;
   int  frame_reg = MIPS_SP_REGNUM;
 
-  CORE_ADDR end_prologue_addr = 0;
+  CORE_ADDR end_prologue_addr;
   int seen_sp_adjust = 0;
   int load_immediate_bytes = 0;
-  int in_delay_slot = 0;
+  int in_delay_slot;
   int regsize_is_64_bits = (mips_abi_regsize (gdbarch) == 8);
 
   /* Can be called when there's no process, and hence when there's no
@@ -3277,13 +3380,23 @@ mips32_scan_prologue (struct gdbarch *gd
     limit_pc = start_pc + 200;
 
 restart:
+  prev_non_prologue_insn = 0;
+  non_prologue_insns = 0;
+  prev_delay_slot = 0;
+  prev_pc = start_pc;
 
+  /* Permit at most one non-prologue non-control-transfer instruction
+     in the middle which may have been reordered by the compiler for
+     optimisation.  */
   frame_offset = 0;
   for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSN32_SIZE)
     {
       unsigned long inst, high_word, low_word;
       int reg;
 
+      this_non_prologue_insn = 0;
+      in_delay_slot = 0;
+
       /* Fetch the instruction.  */
       inst = (unsigned long) mips_fetch_instruction (gdbarch, ISA_MIPS,
 						     cur_pc, NULL);
@@ -3400,6 +3513,7 @@ mips32_scan_prologue (struct gdbarch *gd
          initialize a local variable, so we accept them only before
          a stack adjustment instruction was seen.  */
       else if (!seen_sp_adjust
+	       && !prev_delay_slot
 	       && (high_word == 0x3c01 /* lui $at,n */
 		   || high_word == 0x3c08 /* lui $t0,n */
 		   || high_word == 0x3421 /* ori $at,$at,n */
@@ -3408,31 +3522,32 @@ mips32_scan_prologue (struct gdbarch *gd
 		   || high_word == 0x3408 /* ori $t0,$zero,n */
 		  ))
 	{
-	  if (end_prologue_addr == 0)
-	    load_immediate_bytes += MIPS_INSN32_SIZE;		/* FIXME!  */
+	  load_immediate_bytes += MIPS_INSN32_SIZE;		/* FIXME!  */
+	}
+      /* Check for branches and jumps.  The instruction in the delay
+         slot can be a part of the prologue, so move forward once more.  */
+      else if (mips32_instruction_has_delay_slot (gdbarch, inst))
+	{
+	  in_delay_slot = 1;
 	}
+      /* This instruction is not an instruction typically found
+         in a prologue, so we must have reached the end of the
+         prologue.  */
       else
 	{
-	  /* This instruction is not an instruction typically found
-	     in a prologue, so we must have reached the end of the
-	     prologue.  */
-	  /* FIXME: brobecker/2004-10-10: Can't we just break out of this
-	     loop now?  Why would we need to continue scanning the function
-	     instructions?  */
-	  if (end_prologue_addr == 0)
-	    end_prologue_addr = cur_pc;
-
-	  /* Check for branches and jumps.  For now, only jump to
-	     register are caught (i.e. returns).  */
-	  if ((itype_op (inst) & 0x07) == 0 && rtype_funct (inst) == 8)
-	    in_delay_slot = 1;
+	  this_non_prologue_insn = 1;
 	}
 
-      /* If the previous instruction was a jump, we must have reached
-	 the end of the prologue by now.  Stop scanning so that we do
-	 not go past the function return.  */
-      if (in_delay_slot)
+      non_prologue_insns += this_non_prologue_insn;
+
+      /* A jump or branch, or enough non-prologue insns seen?  If so,
+         then we must have reached the end of the prologue by now.  */
+      if (prev_delay_slot || non_prologue_insns > 1)
 	break;
+
+      prev_non_prologue_insn = this_non_prologue_insn;
+      prev_delay_slot = in_delay_slot;
+      prev_pc = cur_pc;
     }
 
   if (this_cache != NULL)
@@ -3450,14 +3565,12 @@ mips32_scan_prologue (struct gdbarch *gd
 				 + MIPS_RA_REGNUM];
     }
 
-  /* If we didn't reach the end of the prologue when scanning the function
-     instructions, then set end_prologue_addr to the address of the
-     instruction immediately after the last one we scanned.  */
-  /* brobecker/2004-10-10: I don't think this would ever happen, but
-     we may as well be careful and do our best if we have a null
-     end_prologue_addr.  */
-  if (end_prologue_addr == 0)
-    end_prologue_addr = cur_pc;
+  /* Set end_prologue_addr to the address of the instruction immediately
+     after the last one we scanned.  Unless the last one looked like a
+     non-prologue instruction (and we looked ahead), in which case use
+     its address instead.  */
+  end_prologue_addr
+    = prev_non_prologue_insn || prev_delay_slot ? prev_pc : cur_pc;
      
   /* In a frameless function, we might have incorrectly
      skipped some load immediate instructions.  Undo the skipping
@@ -6386,11 +6499,11 @@ mips_single_step_through_delay (struct g
   int size;
 
   if ((mips_pc_is_mips (pc)
-       && !mips32_instruction_has_delay_slot (gdbarch, pc))
+       && !mips32_insn_at_pc_has_delay_slot (gdbarch, pc))
       || (mips_pc_is_micromips (gdbarch, pc)
-	  && !micromips_instruction_has_delay_slot (gdbarch, pc, 0))
+	  && !micromips_insn_at_pc_has_delay_slot (gdbarch, pc, 0))
       || (mips_pc_is_mips16 (gdbarch, pc)
-	  && !mips16_instruction_has_delay_slot (gdbarch, pc, 0)))
+	  && !mips16_insn_at_pc_has_delay_slot (gdbarch, pc, 0)))
     return 0;
 
   isa = mips_pc_isa (gdbarch, pc);
@@ -6980,23 +7093,17 @@ mips_remote_breakpoint_from_pc (struct g
     *kindptr = 4;
 }
 
-/* Return non-zero if the ADDR instruction has a branch delay slot
-   (i.e. it is a jump or branch instruction).  This function is based
-   on mips32_next_pc.  */
+/* Return non-zero if the standard MIPS instruction INST has a branch
+   delay slot (i.e. it is a jump or branch instruction).  This function
+   is based on mips32_next_pc.  */
 
 static int
-mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr)
+mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
 {
-  unsigned long inst;
-  int status;
   int op;
   int rs;
   int rt;
 
-  inst = mips_fetch_instruction (gdbarch, ISA_MIPS, addr, &status);
-  if (status)
-    return 0;
-
   op = itype_op (inst);
   if ((inst & 0xe0000000) != 0)
     {
@@ -7036,98 +7143,139 @@ mips32_instruction_has_delay_slot (struc
       }
 }
 
-/* Return non-zero if the ADDR instruction, which must be a 32-bit
-   instruction if MUSTBE32 is set or can be any instruction otherwise,
-   has a branch delay slot (i.e. it is a non-compact jump instruction).  */
+/* Return non-zero if a standard MIPS instruction at ADDR has a branch
+   delay slot (i.e. it is a jump or branch instruction).  */
 
 static int
-micromips_instruction_has_delay_slot (struct gdbarch *gdbarch,
-				      CORE_ADDR addr, int mustbe32)
+mips32_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr)
 {
   ULONGEST insn;
   int status;
 
-  insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status);
+  insn = mips_fetch_instruction (gdbarch, ISA_MIPS, addr, &status);
   if (status)
     return 0;
 
-				/* 16-bit instructions.  */
-  if ((micromips_op (insn) == 0x11
-				/* POOL16C: bits 010001 */
-       && (b5s5_op (insn) == 0xc
-				/* JR16: bits 010001 01100 */
-	   || (b5s5_op (insn) & 0x1e) == 0xe))
-				/* JALR16, JALRS16: bits 010001 0111x */
-      || (micromips_op (insn) & 0x37) == 0x23
-				/* BEQZ16, BNEZ16: bits 10x011 */
-      || micromips_op (insn) == 0x33)
-				/* B16: bits 110011 */
-    return !mustbe32;
+  return mips32_instruction_has_delay_slot (gdbarch, insn);
+}
 
-				/* 32-bit instructions.  */
-  if (micromips_op (insn) == 0x0)
-				/* POOL32A: bits 000000 */
-    {
-      insn <<= 16;
-      insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status);
-      if (status)
-	return 0;
-      return b0s6_op (insn) == 0x3c
-				/* POOL32Axf: bits 000000 ... 111100 */
-	     && (b6s10_ext (insn) & 0x2bf) == 0x3c;
-				/* JALR, JALR.HB: 000000 000x111100 111100 */
-				/* JALRS, JALRS.HB: 000000 010x111100 111100 */
-    }
+/* Return non-zero if the microMIPS instruction INSN, comprising the
+   16-bit major opcode word in the high 16 bits and any second word
+   in the low 16 bits, has a branch delay slot (i.e. it is a non-compact
+   jump or branch instruction).  The instruction must be 32-bit if
+   MUSTBE32 is set or can be any instruction otherwise.  */
 
-  return (micromips_op (insn) == 0x10
-				/* POOL32I: bits 010000 */
-	  && ((b5s5_op (insn) & 0x1c) == 0x0
+static int
+micromips_instruction_has_delay_slot (ULONGEST insn, int mustbe32)
+{
+  ULONGEST major = insn >> 16;
+
+  switch (micromips_op (major))
+    {
+    /* 16-bit instructions.  */
+    case 0x33:			/* B16: bits 110011 */
+    case 0x2b:			/* BNEZ16: bits 101011 */
+    case 0x23:			/* BEQZ16: bits 100011 */
+      return !mustbe32;
+    case 0x11:			/* POOL16C: bits 010001 */
+      return (!mustbe32
+	      && ((b5s5_op (major) == 0xc
+				/* JR16: bits 010001 01100 */
+		  || (b5s5_op (major) & 0x1e) == 0xe)));
+				/* JALR16, JALRS16: bits 010001 0111x */
+    /* 32-bit instructions.  */
+    case 0x3d:			/* JAL: bits 111101 */
+    case 0x3c:			/* JALX: bits 111100 */
+    case 0x35:			/* J: bits 110101 */
+    case 0x2d:			/* BNE: bits 101101 */
+    case 0x25:			/* BEQ: bits 100101 */
+    case 0x1d:			/* JALS: bits 011101 */
+      return 1;
+    case 0x10:			/* POOL32I: bits 010000 */
+      return ((b5s5_op (major) & 0x1c) == 0x0
 				/* BLTZ, BLTZAL, BGEZ, BGEZAL: 010000 000xx */
-	      || (b5s5_op (insn) & 0x1d) == 0x4
+	      || (b5s5_op (major) & 0x1d) == 0x4
 				/* BLEZ, BGTZ: bits 010000 001x0 */
-	      || (b5s5_op (insn) & 0x1d) == 0x11
+	      || (b5s5_op (major) & 0x1d) == 0x11
 				/* BLTZALS, BGEZALS: bits 010000 100x1 */
-	      || ((b5s5_op (insn) & 0x1e) == 0x14
-		  && (insn & 0x3) == 0x0)
+	      || ((b5s5_op (major) & 0x1e) == 0x14
+		  && (major & 0x3) == 0x0)
 				/* BC2F, BC2T: bits 010000 1010x xxx00 */
-	      || (b5s5_op (insn) & 0x1e) == 0x1a
+	      || (b5s5_op (major) & 0x1e) == 0x1a
 				/* BPOSGE64, BPOSGE32: bits 010000 1101x */
-	      || ((b5s5_op (insn) & 0x1e) == 0x1c
-		  && (insn & 0x3) == 0x0)
+	      || ((b5s5_op (major) & 0x1e) == 0x1c
+		  && (major & 0x3) == 0x0)
 				/* BC1F, BC1T: bits 010000 1110x xxx00 */
-	      || ((b5s5_op (insn) & 0x1c) == 0x1c
-		  && (insn & 0x3) == 0x1)))
+	      || ((b5s5_op (major) & 0x1c) == 0x1c
+		  && (major & 0x3) == 0x1));
 				/* BC1ANY*: bits 010000 111xx xxx01 */
-	 || (micromips_op (insn) & 0x1f) == 0x1d
-				/* JALS, JAL: bits x11101 */
-	 || (micromips_op (insn) & 0x37) == 0x25
-				/* BEQ, BNE: bits 10x101 */
-	 || micromips_op (insn) == 0x35
-				/* J: bits 110101 */
-	 || micromips_op (insn) == 0x3c;
-				/* JALX: bits 111100 */
+    case 0x0:			/* POOL32A: bits 000000 */
+      return (b0s6_op (insn) == 0x3c
+				/* POOL32Axf: bits 000000 ... 111100 */
+	      && (b6s10_ext (insn) & 0x2bf) == 0x3c);
+				/* JALR, JALR.HB: 000000 000x111100 111100 */
+				/* JALRS, JALRS.HB: 000000 010x111100 111100 */
+    default:
+      return 0;
+    }
 }
 
-/* Return non-zero if a MIPS16 instruction at ADDR has a branch delay
+/* Return non-zero if a microMIPS instruction at ADDR has a branch delay
    slot (i.e. it is a non-compact jump instruction).  The instruction
    must be 32-bit if MUSTBE32 is set or can be any instruction otherwise.  */
 
 static int
-mips16_instruction_has_delay_slot (struct gdbarch *gdbarch, CORE_ADDR addr,
-				   int mustbe32)
+micromips_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
+				     CORE_ADDR addr, int mustbe32)
 {
-  unsigned short inst;
+  ULONGEST insn;
   int status;
 
-  inst = mips_fetch_instruction (gdbarch, ISA_MIPS16, addr, &status);
+  insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status);
   if (status)
     return 0;
+  insn <<= 16;
+  if (mips_insn_size (ISA_MICROMIPS, insn) == 2 * MIPS_INSN16_SIZE)
+    {
+      insn |= mips_fetch_instruction (gdbarch, ISA_MICROMIPS, addr, &status);
+      if (status)
+	return 0;
+    }
+
+  return micromips_instruction_has_delay_slot (insn, mustbe32);
+}
 
+/* Return non-zero if the MIPS16 instruction INST, which must be
+   a 32-bit instruction if MUSTBE32 is set or can be any instruction
+   otherwise, has a branch delay slot (i.e. it is a non-compact jump
+   instruction).  This function is based on mips16_next_pc.  */
+
+static int
+mips16_instruction_has_delay_slot (unsigned short inst, int mustbe32)
+{
   if ((inst & 0xf89f) == 0xe800)	/* JR/JALR (16-bit instruction)  */
     return !mustbe32;
   return (inst & 0xf800) == 0x1800;	/* JAL/JALX (32-bit instruction)  */
 }
 
+/* Return non-zero if a MIPS16 instruction at ADDR has a branch delay
+   slot (i.e. it is a non-compact jump instruction).  The instruction
+   must be 32-bit if MUSTBE32 is set or can be any instruction otherwise.  */
+
+static int
+mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
+				  CORE_ADDR addr, int mustbe32)
+{
+  unsigned short insn;
+  int status;
+
+  insn = mips_fetch_instruction (gdbarch, ISA_MIPS16, addr, &status);
+  if (status)
+    return 0;
+
+  return mips16_instruction_has_delay_slot (insn, mustbe32);
+}
+
 /* Calculate the starting address of the MIPS memory segment BPADDR is in.
    This assumes KSSEG exists.  */
 
@@ -7219,12 +7367,12 @@ mips_adjust_breakpoint_address (struct g
       /* If the previous instruction has a branch delay slot, we have
          to move the breakpoint to the branch instruction. */
       prev_addr = bpaddr - 4;
-      if (mips32_instruction_has_delay_slot (gdbarch, prev_addr))
+      if (mips32_insn_at_pc_has_delay_slot (gdbarch, prev_addr))
 	bpaddr = prev_addr;
     }
   else
     {
-      int (*instruction_has_delay_slot) (struct gdbarch *, CORE_ADDR, int);
+      int (*insn_at_pc_has_delay_slot) (struct gdbarch *, CORE_ADDR, int);
       CORE_ADDR addr, jmpaddr;
       int i;
 
@@ -7238,9 +7386,9 @@ mips_adjust_breakpoint_address (struct g
          2 bytes, so the idea is the same.
          FIXME: We have to assume that bpaddr is not the second half
          of an extended instruction.  */
-      instruction_has_delay_slot = (mips_pc_is_micromips (gdbarch, bpaddr)
-				     ? micromips_instruction_has_delay_slot
-				     : mips16_instruction_has_delay_slot);
+      insn_at_pc_has_delay_slot = (mips_pc_is_micromips (gdbarch, bpaddr)
+				   ? micromips_insn_at_pc_has_delay_slot
+				   : mips16_insn_at_pc_has_delay_slot);
 
       jmpaddr = 0;
       addr = bpaddr;
@@ -7249,12 +7397,12 @@ mips_adjust_breakpoint_address (struct g
 	  if (unmake_compact_addr (addr) == boundary)
 	    break;
 	  addr -= MIPS_INSN16_SIZE;
-	  if (i == 1 && instruction_has_delay_slot (gdbarch, addr, 0))
+	  if (i == 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 0))
 	    /* Looks like a JR/JALR at [target-1], but it could be
 	       the second word of a previous JAL/JALX, so record it
 	       and check back one more.  */
 	    jmpaddr = addr;
-	  else if (i > 1 && instruction_has_delay_slot (gdbarch, addr, 1))
+	  else if (i > 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 1))
 	    {
 	      if (i == 2)
 		/* Looks like a JAL/JALX at [target-2], but it could also


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]