[PATCH v2 12/12] MIPS: Fix stepping through instructions branching to own delay slot

"Maciej W. Rozycki" <[email protected]>
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
From: Maciej W. Rozycki <[email protected]>

Branching to own delay slot is not disallowed in the ISA and works as 
expected.  Especially for regular conditional branches it might be a 
useful way to have the branch delay instruction conditionally executed 
once or twice, which in some cases could avoid extra jumping around.

We do not handle this case specially in software stepping, which means 
that for a taken branch or a jump a single-stepping breakpoint is placed 
in the delay slot.  This is also allowed by the ISA, however it causes 
the execution of the containing branch to be abandoned and a breakpoint 
exception triggered with the PC pointing at the branch.  While the BD 
bit is also set in the CP0 Cause register indicating the situation, we 
do not examine it, following a deliberate design decision.

Consequently single-stepping over such a branch or jump loops forever 
and execution progress cannot be made without manual intervention.

Recognize the situation then, and skip over the delay slot when placing 
a single-stepping breakpoint, except for jumps that switch the ISA mode 
(for which jumping to the delay slot instruction seems of questionable 
use, even though valid) and where the delay slot instruction itself is a 
branch or jump (which yields unpredictable operation according to the 
ISA and is therefore unsupported; actual hardware implementations vary 
from triggering the Reserved Instruction exception, through executing 
the delay slot branch or jump instruction normally, to ignoring it and 
proceeding from the target of the original branch or jump).

Add testcases accordingly, for MIPS I, MIPS II, MIPS16 and microMIPS CPU 
branches and jumps, and DSP ASE branches except for 64-bit ones.

Approved-By: Maciej W. Rozycki <[email protected]>
---
New change in v2.
---
 gdb/mips-tdep.c                                   |  223 +++++++++++++++-------
 gdb/testsuite/gdb.arch/micromips-branch-delay.c   |   89 ++++++++
 gdb/testsuite/gdb.arch/micromips-branch-delay.exp |   31 +++
 gdb/testsuite/gdb.arch/micromips-jals-delay.c     |   56 +++++
 gdb/testsuite/gdb.arch/micromips-jals-delay.exp   |   30 ++
 gdb/testsuite/gdb.arch/mips-allow.exp.tcl         |   58 +++++
 gdb/testsuite/gdb.arch/mips-dsp-branch-delay.c    |   62 ++++++
 gdb/testsuite/gdb.arch/mips-dsp-branch-delay.exp  |   31 +++
 gdb/testsuite/gdb.arch/mips-jal-delay.c           |   60 +++++
 gdb/testsuite/gdb.arch/mips-jal-delay.exp         |   27 ++
 gdb/testsuite/gdb.arch/mips-jr-delay.c            |   64 ++++++
 gdb/testsuite/gdb.arch/mips-jr-delay.exp          |   23 ++
 gdb/testsuite/gdb.arch/mips1-bal-delay.c          |   63 ++++++
 gdb/testsuite/gdb.arch/mips1-bal-delay.exp        |   31 +++
 gdb/testsuite/gdb.arch/mips1-branch-delay.c       |   67 ++++++
 gdb/testsuite/gdb.arch/mips1-branch-delay.exp     |   31 +++
 gdb/testsuite/gdb.arch/mips2-branch-delay.c       |   70 ++++++
 gdb/testsuite/gdb.arch/mips2-branch-delay.exp     |   31 +++
 18 files changed, 980 insertions(+), 67 deletions(-)

gdb-mips-next-pc-bds.diff
Index: binutils-gdb/gdb/mips-tdep.c
===================================================================
--- binutils-gdb.orig/gdb/mips-tdep.c
+++ binutils-gdb/gdb/mips-tdep.c
@@ -1592,6 +1592,7 @@ mips32_bc1_pc (struct gdbarch *gdbarch,
   int cnum = (itype_rt (inst) >> 2) & (count - 1);
   int tf = itype_rt (inst) & 1;
   int mask = (1 << count) - 1;
+  CORE_ADDR pc_adj;
   ULONGEST fcs;
   int cond;
 
@@ -1602,8 +1603,9 @@ mips32_bc1_pc (struct gdbarch *gdbarch,
   fcs = regcache_raw_get_unsigned (regcache, fcsr);
   cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01);
 
-  if (((cond >> cnum) & mask) != mask * !tf)
-    pc += mips32_relative_offset (inst);
+  pc_adj = mips32_relative_offset (inst);
+  if (pc_adj && ((cond >> cnum) & mask) != mask * !tf)
+    pc += pc_adj;
   else
     pc += 4;
 
@@ -1647,6 +1649,7 @@ mips32_next_pc (struct regcache *regcach
 {
   struct gdbarch *gdbarch = regcache->arch ();
   unsigned long inst;
+  CORE_ADDR pc_adj;
   int op;
   inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL);
   op = itype_op (inst);
@@ -1702,10 +1705,12 @@ mips32_next_pc (struct regcache *regcach
 	  if (op == 54 || op == 62)
 	    bit += 32;
 
-	  if (((regcache_raw_get_signed (regcache,
-					 itype_rs (inst)) >> bit) & 1)
-	      == branch_if)
-	    pc += mips32_relative_offset (inst) + 4;
+	  pc_adj = mips32_relative_offset (inst);
+	  if (pc_adj
+	      && ((regcache_raw_get_signed (regcache,
+					    itype_rs (inst)) >> bit) & 1)
+		  == branch_if)
+	    pc += pc_adj + 4;
 	  else
 	    pc += 8;		/* After the delay slot.  */
 	}
@@ -1724,8 +1729,12 @@ mips32_next_pc (struct regcache *regcach
 	    {
 	    case 8:		/* JR */
 	    case 9:		/* JALR */
-	      /* Set PC to that address.  */
-	      pc = regcache_raw_get_signed (regcache, rtype_rs (inst));
+	      /* Set PC to that address, avoiding the delay slot.  */
+	      pc_adj = regcache_raw_get_signed (regcache, rtype_rs (inst));
+	      if (pc_adj != pc + 4)
+		pc = pc_adj;
+	      else
+		pc += 8;
 	      break;
 	    case 12:		/* SYSCALL */
 	      {
@@ -1752,8 +1761,10 @@ mips32_next_pc (struct regcache *regcach
 	      case 2:		/* BLTZL */
 	      case 16:		/* BLTZAL */
 	      case 18:		/* BLTZALL */
-		if (regcache_raw_get_signed (regcache, itype_rs (inst)) < 0)
-		  pc += mips32_relative_offset (inst) + 4;
+		pc_adj = mips32_relative_offset (inst);
+		if (pc_adj
+		    && regcache_raw_get_signed (regcache, itype_rs (inst)) < 0)
+		  pc += pc_adj + 4;
 		else
 		  pc += 8;	/* After the delay slot.  */
 		break;
@@ -1761,8 +1772,10 @@ mips32_next_pc (struct regcache *regcach
 	      case 3:		/* BGEZL */
 	      case 17:		/* BGEZAL */
 	      case 19:		/* BGEZALL */
-		if (regcache_raw_get_signed (regcache, itype_rs (inst)) >= 0)
-		  pc += mips32_relative_offset (inst) + 4;
+		pc_adj = mips32_relative_offset (inst);
+		if (pc_adj
+		    && regcache_raw_get_signed (regcache, itype_rs (inst)) >= 0)
+		  pc += pc_adj + 4;
 		else
 		  pc += 8;	/* After the delay slot.  */
 		break;
@@ -1778,9 +1791,11 @@ mips32_next_pc (struct regcache *regcach
 		      /* No way to handle; it'll most likely trap anyway.  */
 		      break;
 
-		    if ((regcache_raw_get_unsigned (regcache,
-						    dspctl) & 0x7f) >= pos)
-		      pc += mips32_relative_offset (inst);
+		    pc_adj = mips32_relative_offset (inst);
+		    if (pc_adj
+			&& (regcache_raw_get_unsigned (regcache,
+						       dspctl) & 0x7f) >= pos)
+		      pc += pc_adj;
 		    else
 		      pc += 4;
 		  }
@@ -1797,37 +1812,49 @@ mips32_next_pc (struct regcache *regcach
 	    unsigned long reg;
 	    reg = jtype_target (inst) << 2;
 	    /* Upper four bits get never changed...  */
-	    pc = reg + ((pc + 4) & ~(CORE_ADDR) 0x0fffffff);
+	    pc_adj = reg + ((pc + 4) & ~(CORE_ADDR) 0x0fffffff);
+	    if (pc_adj != pc + 4)
+	      pc = pc_adj;
+	    else
+	      pc += 8;
 	  }
 	  break;
 	case 4:		/* BEQ, BEQL */
 	equal_branch:
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) ==
-	      regcache_raw_get_signed (regcache, itype_rt (inst)))
-	    pc += mips32_relative_offset (inst) + 4;
+	  pc_adj = mips32_relative_offset (inst);
+	  if (pc_adj
+	      && (regcache_raw_get_signed (regcache, itype_rs (inst))
+		  == regcache_raw_get_signed (regcache, itype_rt (inst))))
+	    pc += pc_adj + 4;
 	  else
 	    pc += 8;
 	  break;
 	case 5:		/* BNE, BNEL */
 	neq_branch:
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) !=
-	      regcache_raw_get_signed (regcache, itype_rt (inst)))
-	    pc += mips32_relative_offset (inst) + 4;
+	  pc_adj = mips32_relative_offset (inst);
+	  if (pc_adj
+	      && (regcache_raw_get_signed (regcache, itype_rs (inst))
+		  != regcache_raw_get_signed (regcache, itype_rt (inst))))
+	    pc += pc_adj + 4;
 	  else
 	    pc += 8;
 	  break;
 	case 6:		/* BLEZ, BLEZL */
 	less_equal_branch:
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) <= 0)
-	    pc += mips32_relative_offset (inst) + 4;
+	  pc_adj = mips32_relative_offset (inst);
+	  if (pc_adj
+	      && regcache_raw_get_signed (regcache, itype_rs (inst)) <= 0)
+	    pc += pc_adj + 4;
 	  else
 	    pc += 8;
 	  break;
 	case 7:		/* BGTZ, BGTZL */
 	default:
 	greater_branch:
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) > 0)
-	    pc += mips32_relative_offset (inst) + 4;
+	  pc_adj = mips32_relative_offset (inst);
+	  if (pc_adj
+	      && regcache_raw_get_signed (regcache, itype_rs (inst)) > 0)
+	    pc += pc_adj + 4;
 	  else
 	    pc += 8;
 	  break;
@@ -1887,6 +1914,7 @@ micromips_bc1_pc (struct gdbarch *gdbarc
   int cnum = b2s3_cc (insn >> 16) & (count - 1);
   int tf = b5s5_op (insn >> 16) & 1;
   int mask = (1 << count) - 1;
+  CORE_ADDR pc_adj;
   ULONGEST fcs;
   int cond;
 
@@ -1897,8 +1925,9 @@ micromips_bc1_pc (struct gdbarch *gdbarc
   fcs = regcache_raw_get_unsigned (regcache, fcsr);
   cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01);
 
-  if (((cond >> cnum) & mask) != mask * !tf)
-    pc += micromips_relative_offset16 (insn);
+  pc_adj = micromips_relative_offset16 (insn);
+  if (pc_adj && ((cond >> cnum) & mask) != mask * !tf)
+    pc += pc_adj;
   else
     pc += micromips_pc_insn_size (gdbarch, pc);
 
@@ -1912,6 +1941,7 @@ static CORE_ADDR
 micromips_next_pc (struct regcache *regcache, CORE_ADDR pc)
 {
   struct gdbarch *gdbarch = regcache->arch ();
+  CORE_ADDR pc_adj;
   ULONGEST insn;
 
   insn = mips_fetch_instruction (gdbarch, ISA_MICROMIPS, pc, NULL);
@@ -1935,8 +1965,12 @@ micromips_next_pc (struct regcache *regc
 		case 0x7c:  /* JALR.HB:  000000 0001111100 111100 */
 		case 0x13c: /* JALRS:    000000 0100111100 111100 */
 		case 0x17c: /* JALRS.HB: 000000 0101111100 111100 */
-		  pc = regcache_raw_get_signed (regcache,
-						b0s5_reg (insn >> 16));
+		  pc_adj = regcache_raw_get_signed (regcache,
+						    b0s5_reg (insn >> 16));
+		  if (pc_adj != pc)
+		    pc = pc_adj;
+		  else
+		    pc += micromips_pc_insn_size (gdbarch, pc);;
 		  break;
 		case 0x22d: /* SYSCALL:  000000 1000101101 111100 */
 		  {
@@ -1958,9 +1992,11 @@ micromips_next_pc (struct regcache *regc
 	    case 0x00: /* BLTZ: bits 010000 00000 */
 	    case 0x01: /* BLTZAL: bits 010000 00001 */
 	    case 0x11: /* BLTZALS: bits 010000 10001 */
-	      if (regcache_raw_get_signed (regcache,
-					   b0s5_reg (insn >> 16)) < 0)
-		pc += micromips_relative_offset16 (insn);
+	      pc_adj = micromips_relative_offset16 (insn);
+	      if (pc_adj
+		  && regcache_raw_get_signed (regcache,
+					      b0s5_reg (insn >> 16)) < 0)
+		pc += pc_adj;
 	      else
 		pc += micromips_pc_insn_size (gdbarch, pc);
 	      break;
@@ -1968,17 +2004,21 @@ micromips_next_pc (struct regcache *regc
 	    case 0x02: /* BGEZ: bits 010000 00010 */
 	    case 0x03: /* BGEZAL: bits 010000 00011 */
 	    case 0x13: /* BGEZALS: bits 010000 10011 */
-	      if (regcache_raw_get_signed (regcache,
-					   b0s5_reg (insn >> 16)) >= 0)
-		pc += micromips_relative_offset16 (insn);
+	      pc_adj = micromips_relative_offset16 (insn);
+	      if (pc_adj
+		  && regcache_raw_get_signed (regcache,
+					      b0s5_reg (insn >> 16)) >= 0)
+		pc += pc_adj;
 	      else
 		pc += micromips_pc_insn_size (gdbarch, pc);
 	      break;
 
 	    case 0x04: /* BLEZ: bits 010000 00100 */
-	      if (regcache_raw_get_signed (regcache,
-					   b0s5_reg (insn >> 16)) <= 0)
-		pc += micromips_relative_offset16 (insn);
+	      pc_adj = micromips_relative_offset16 (insn);
+	      if (pc_adj
+		  && regcache_raw_get_signed (regcache,
+					      b0s5_reg (insn >> 16)) <= 0)
+		pc += pc_adj;
 	      else
 		pc += micromips_pc_insn_size (gdbarch, pc);
 	      break;
@@ -1990,9 +2030,11 @@ micromips_next_pc (struct regcache *regc
 	      break;
 
 	    case 0x06: /* BGTZ: bits 010000 00110 */
-	      if (regcache_raw_get_signed (regcache,
-					   b0s5_reg (insn >> 16)) > 0)
-		pc += micromips_relative_offset16 (insn);
+	      pc_adj = micromips_relative_offset16 (insn);
+	      if (pc_adj
+		  && regcache_raw_get_signed (regcache,
+					      b0s5_reg (insn >> 16)) > 0)
+		pc += pc_adj;
 	      else
 		pc += micromips_pc_insn_size (gdbarch, pc);
 	      break;
@@ -2020,9 +2062,11 @@ micromips_next_pc (struct regcache *regc
 		  /* No way to handle; it'll most likely trap anyway.  */
 		  break;
 
-		if ((regcache_raw_get_unsigned (regcache,
-						dspctl) & 0x7f) >= pos)
-		  pc += micromips_relative_offset16 (insn);
+		pc_adj = micromips_relative_offset16 (insn);
+		if (pc_adj
+		    && (regcache_raw_get_unsigned (regcache,
+						   dspctl) & 0x7f) >= pos)
+		  pc += pc_adj;
 		else
 		  pc += micromips_pc_insn_size (gdbarch, pc);
 	      }
@@ -2048,21 +2092,29 @@ micromips_next_pc (struct regcache *regc
 	case 0x1d: /* JALS: bits 011101 */
 	case 0x35: /* J: bits 110101 */
 	case 0x3d: /* JAL: bits 111101 */
-	  pc = ((pc | 0x7fffffe) ^ 0x7fffffe) | (b0s26_imm (insn) << 1);
+	  pc_adj = ((pc | 0x7fffffe) ^ 0x7fffffe) | (b0s26_imm (insn) << 1);
+	  if (pc_adj != pc)
+	    pc = pc_adj;
+	  else
+	    pc += micromips_pc_insn_size (gdbarch, pc);;
 	  break;
 
 	case 0x25: /* BEQ: bits 100101 */
-	  if (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
-	      == regcache_raw_get_signed (regcache, b5s5_reg (insn >> 16)))
-	    pc += micromips_relative_offset16 (insn);
+	  pc_adj = micromips_relative_offset16 (insn);
+	  if (pc_adj
+	      && (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
+		  == regcache_raw_get_signed (regcache, b5s5_reg (insn >> 16))))
+	    pc += pc_adj;
 	  else
 	    pc += micromips_pc_insn_size (gdbarch, pc);
 	  break;
 
 	case 0x2d: /* BNE: bits 101101 */
-	  if (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
-	      != regcache_raw_get_signed (regcache, b5s5_reg (insn >> 16)))
-	    pc += micromips_relative_offset16 (insn);
+	  pc_adj = micromips_relative_offset16 (insn);
+	  if (pc_adj
+	      && (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
+		  != regcache_raw_get_signed (regcache, b5s5_reg (insn >> 16))))
+	    pc += pc_adj;
 	  else
 	    pc += micromips_pc_insn_size (gdbarch, pc);
 	  break;
@@ -2078,20 +2130,33 @@ micromips_next_pc (struct regcache *regc
       switch (micromips_op (insn))
 	{
 	case 0x11: /* POOL16C: bits 010001 */
-	  if ((b5s5_op (insn) & 0x1c) == 0xc)
-	    /* JR16, JRC, JALR16, JALRS16: 010001 011xx */
-	    pc = regcache_raw_get_signed (regcache, b0s5_reg (insn));
-	  else if (b5s5_op (insn) == 0x18)
-	    /* JRADDIUSP: bits 010001 11000 */
-	    pc = regcache_raw_get_signed (regcache, MIPS_RA_REGNUM);
+	  switch (b5s5_op (insn))
+	    {
+	    case 0x0c: /* JR16: bits 010001 01100 */
+	    case 0x0e: /* JALR16: bits 010001 01110 */
+	    case 0x0f: /* JALRS16: bits 010001 01111 */
+	      pc_adj = regcache_raw_get_signed (regcache, b0s5_reg (insn));
+	      if (pc_adj != pc)
+		pc = pc_adj;
+	      else
+		pc += micromips_pc_insn_size (gdbarch, pc);;
+	      break;
+	    case 0x0d: /* JRC: bits 010001 01101 */
+	      pc = regcache_raw_get_signed (regcache, b0s5_reg (insn));
+	      break;
+	    case 0x18: /* JRADDIUSP: bits 010001 11000 */
+	      pc = regcache_raw_get_signed (regcache, MIPS_RA_REGNUM);
+	      break;
+	    }
 	  break;
 
 	case 0x23: /* BEQZ16: bits 100011 */
 	  {
 	    int rs = mips_reg3_to_reg[b7s3_reg (insn)];
 
-	    if (regcache_raw_get_signed (regcache, rs) == 0)
-	      pc += micromips_relative_offset7 (insn);
+	    pc_adj = micromips_relative_offset7 (insn);
+	    if (pc_adj && regcache_raw_get_signed (regcache, rs) == 0)
+	      pc += pc_adj;
 	    else
 	      pc += micromips_pc_insn_size (gdbarch, pc);
 	  }
@@ -2101,15 +2166,20 @@ micromips_next_pc (struct regcache *regc
 	  {
 	    int rs = mips_reg3_to_reg[b7s3_reg (insn)];
 
-	    if (regcache_raw_get_signed (regcache, rs) != 0)
-	      pc += micromips_relative_offset7 (insn);
+	    pc_adj = micromips_relative_offset7 (insn);
+	    if (pc_adj && regcache_raw_get_signed (regcache, rs) != 0)
+	      pc += pc_adj;
 	    else
 	      pc += micromips_pc_insn_size (gdbarch, pc);
 	  }
 	  break;
 
 	case 0x33: /* B16: bits 110011 */
-	  pc += micromips_relative_offset10 (insn);
+	  pc_adj = micromips_relative_offset10 (insn);
+	  if (pc_adj)
+	    pc += pc_adj;
+	  else
+	    pc += micromips_pc_insn_size (gdbarch, pc);;
 	  break;
 	}
       break;
@@ -2278,12 +2348,25 @@ add_offset_16 (CORE_ADDR pc, int offset)
   return pc + (offset << 1) + 2;
 }
 
+/* Return the size in bytes of the MIPS16 instruction at the address PC.  */
+
+static int
+mips16_pc_insn_size (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  ULONGEST insn;
+
+  insn = mips_fetch_instruction (gdbarch, ISA_MIPS16, pc, NULL);
+  return mips_insn_size (ISA_MIPS16, insn);
+}
+
 static CORE_ADDR
 extended_mips16_next_pc (regcache *regcache, CORE_ADDR pc,
 			 unsigned int extension, unsigned int insn)
 {
   struct gdbarch *gdbarch = regcache->arch ();
   int op = (insn >> 11);
+  CORE_ADDR pc_adj;
+
   switch (op)
     {
     case 2:			/* Branch */
@@ -2298,11 +2381,13 @@ extended_mips16_next_pc (regcache *regca
       {
 	struct upk_mips16 upk;
 	unpack_mips16 (gdbarch, pc, extension, insn, jalxtype, &upk);
-	pc = ((pc + 4) & (~(CORE_ADDR) 0x0fffffff)) | (upk.offset << 2);
+	pc_adj = ((pc + 4) & (~(CORE_ADDR) 0x0fffffff)) | (upk.offset << 2);
 	if ((insn >> 10) & 0x01)	/* Exchange mode */
-	  pc = pc & ~0x01;	/* Clear low bit, indicate 32 bit mode.  */
+	  pc = pc_adj & ~0x01;	/* Clear low bit, indicate 32 bit mode.  */
+	else if ((pc_adj | 0x01) != pc + 4)
+	  pc = pc_adj | 0x01;
 	else
-	  pc |= 0x01;
+	  pc += 4 + mips16_pc_insn_size (gdbarch, pc + 4);
 	break;
       }
     case 4:			/* beqz */
@@ -2358,7 +2443,11 @@ extended_mips16_next_pc (regcache *regca
 	      reg = mips_reg3_to_reg[upk.regx];
 	    else
 	      reg = 31;		/* Function return instruction.  */
-	    pc = regcache_raw_get_signed (regcache, reg);
+	    pc_adj = regcache_raw_get_signed (regcache, reg);
+	    if ((insn >> 7) & 0x01 || pc_adj != pc + 2)
+	      pc = pc_adj;
+	    else
+	      pc += 2 + mips16_pc_insn_size (gdbarch, pc + 2);
 	  }
 	else
 	  pc += 2;
Index: binutils-gdb/gdb/testsuite/gdb.arch/micromips-branch-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/micromips-branch-delay.c
@@ -0,0 +1,89 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through microMIPS branch and jump instructions
+   to their delay slot.  */
+
+int
+test_micromips_branch_delay (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  int mone = -1;
+  int zero = 0;
+
+  extern const char jr_one asm("jr_one");
+  extern const char jr_two asm("jr_two");
+  extern const char jr_thr asm("jr_thr");
+  extern const char jr_for asm("jr_for");
+
+  asm volatile (
+	".macro	b_test op, args:vararg\n\t"
+	".ifb	\\args\n\t"
+	"\\op	0f\n\t"
+	".else\n\t"
+	"\\op	\\args, 0f\n\t"
+	".endif\n\t"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".macro	j_test op, reg, label\n\t"
+	"\\op	\\reg\n\t"
+	".globl	\\label\n\t"
+	".type	\\label, @function\n"
+	"\\label:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"				/* Units: steps.  */
+	"nop\n\t"					/* NOP:       1s  */
+	"b_test	b16\n\t"				/* Taken:     1s  */
+	"b_test	beqz16, %[zero]\n\t"			/* Taken:     1s  */
+	"b_test	bnez16, %[mone]\n\t"			/* Taken:     1s  */
+	"b_test	bltzals, %[mone]\n\t"			/* Taken:     1s  */
+	"b_test	bgezals, %[zero]\n\t"			/* Taken:     1s  */
+	"j_test	jalr16, %[jr_one], jr_one\n\t"		/* Taken:     1s  */
+	"j_test	jalrs16, %[jr_two], jr_two\n\t"		/* Taken:     1s  */
+	"j_test	jalrs, %[jr_thr], jr_thr\n\t"		/* Taken:     1s  */
+	"j_test	jalrs.hb, %[jr_for], jr_for\n\t"	/* Taken:     1s  */
+	"nop\n\t"					/* NOP:       1s  */
+	".globl	step_stop\n\t"				/* Total:    11s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [mone] "r" (mone), [zero] "r" (zero)
+	  [jr_one] "r" (&jr_one), [jr_two] "r" (&jr_two),
+	  [jr_thr] "r" (&jr_thr), [jr_for] "r" (&jr_for)
+	: "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_micromips_branch_delay ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/micromips-branch-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/micromips-branch-delay.exp
@@ -0,0 +1,31 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through microMIPS branch and jump instructions
+# to their delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_micromips_ase_tests
+
+standard_testfile
+
+set steps 11
+foreach flag {-Wa,-W -mno-mips16 -mmicromips -minterlink-compressed} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/micromips-jals-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/micromips-jals-delay.c
@@ -0,0 +1,56 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through microMIPS JALS to its delay slot.  */
+
+int
+test_micromips_jals (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  asm volatile (
+	".macro	j_test op\n\t"
+	"\\op	0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"j_test	jals\n\t"			/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     3s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	: : : "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_micromips_jals ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/micromips-jals-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/micromips-jals-delay.exp
@@ -0,0 +1,30 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through microMIPS JALS to its delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_micromips_jals_tests
+
+standard_testfile
+
+set steps 3
+foreach flag {-Wa,-W -mno-mips16 -mmicromips -minterlink-compressed} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-allow.exp.tcl
===================================================================
--- binutils-gdb.orig/gdb/testsuite/gdb.arch/mips-allow.exp.tcl
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-allow.exp.tcl
@@ -15,6 +15,37 @@
 
 # Feature availability check helpers for MIPS tests.
 
+# Check for JAL machine instruction support.
+#
+# The regular MIPS and microMIPS JAL assembly instruction is always
+# a macro, however in the non-PIC assembly mode it produces a single
+# machine instruction.  In PIC/PIE assembly modes it expands to a PIC
+# call sequence using JALR instead.  Make sure an actual JAL machine
+# instruction is produced and no macro expanded.
+#
+# MIPS16 JAL requires its jump target to be 32-bit aligned, so use
+# `.align' to satisfy this constraint.
+proc allow_mips_jal_tests {} {
+    return [allow_target_tests "allow_mips_jal_tests" \
+	"MIPS JAL support" "Segmentation fault" \
+	    {
+		int main() {
+		    asm volatile (
+			".set	push\n\t"
+			".set	noreorder\n\t"
+			".set	nomacro\n\t"
+			"jal	0f\n\t"
+			" nop\n\t"
+			".align	2\n"
+			"0:\n\t"
+			".set	pop\n"
+			: : : "$31");
+		    return 0;
+		}
+	    } \
+	{-Wa,-fatal-warnings}]
+}
+
 # Check for MIPS I branch support.  These instructions may be absent,
 # such as with MIPS16 compilations.  Make sure an actual BLTZ machine
 # instruction is produced and no macro expanded.
@@ -151,5 +182,32 @@ proc allow_micromips_ase_tests {} {
 		    return 0;
 		}
 	    } \
+	{-Wa,-fatal-warnings -mno-mips16 -mmicromips -minterlink-compressed}]
+}
+
+# Check for microMIPS JALS machine instruction support.
+#
+# The microMIPS JALS assembly instruction is always a macro, however
+# in the non-PIC assembly mode it produces a single machine instruction.
+# In PIC/PIE assembly modes it expands to a PIC call sequence using
+# JALRS instead.  Make sure an actual JALS machine instruction is
+# produced and no macro expanded.
+proc allow_micromips_jals_tests {} {
+    return [allow_target_tests "allow_mips_jals_tests" \
+	"microMIPS JALS support" "Segmentation fault" \
+	    {
+		int main() {
+		    asm volatile (
+			".set	push\n\t"
+			".set	noreorder\n\t"
+			".set	nomacro\n\t"
+			"jals	0f\n\t"
+			" nop\n\t"
+			"0:\n\t"
+			".set	pop\n"
+			: : : "$31");
+		    return 0;
+		}
+	    } \
 	{-Wa,-fatal-warnings -mno-mips16 -mmicromips -minterlink-compressed}]
 }
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-dsp-branch-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-dsp-branch-delay.c
@@ -0,0 +1,62 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through MIPS DSP branch instructions
+   to their delay slot.  */
+
+int
+test_mips_dsp_branch_delay (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  int thr_two = 32;
+
+  asm volatile (
+	".macro	b_test op\n\t"
+	"\\op	0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"wrdsp	%[thr_two], 1\n\t"		/* WRDSP:     1s  */
+	"b_test	bposge32\n\t"			/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     4s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [thr_two] "r" (thr_two)
+	: "$dsp_po");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips_dsp_branch_delay ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-dsp-branch-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-dsp-branch-delay.exp
@@ -0,0 +1,31 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through MIPS DSP branch instructions
+# to their delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_mips_dsp_ase_tests
+
+standard_testfile
+
+set steps 4
+foreach flag {-mno-mips16 -minterlink-compressed -mdsp} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-jal-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-jal-delay.c
@@ -0,0 +1,60 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through JAL to its delay slot.  */
+
+int
+test_mips_jal (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  /* MIPS16 JAL requires its jump target to be 32-bit aligned, so use
+     `.align' and two leading NOP instructions to meet this constraint.  */
+  asm volatile (
+	".macro	j_test op\n\t"
+	"\\op	0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".align	2\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	"j_test	jal\n\t"			/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     4s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	: : : "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips_jal ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-jal-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-jal-delay.exp
@@ -0,0 +1,27 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through JAL to its delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_mips_jal_tests
+
+standard_testfile
+
+set steps 4
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-jr-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-jr-delay.c
@@ -0,0 +1,64 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through JR to its delay slot.  */
+
+int
+test_mips_jr (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  extern const char jr_one asm("jr_one");
+  extern const char jr_two asm("jr_two");
+
+  asm volatile (
+	".macro	j_test op, reg, label\n\t"
+	"\\op	\\reg\n\t"
+	".globl	\\label\n\t"
+	".type	\\label, @function\n"
+	"\\label:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"j_test	jr, %[jr_one], jr_one\n\t"	/* Taken:     1s  */
+	"j_test	jalr, %[jr_two], jr_two\n\t"	/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     4s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [jr_one] "r" (&jr_one), [jr_two] "r" (&jr_two)
+	: "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips_jr ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips-jr-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips-jr-delay.exp
@@ -0,0 +1,23 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through JR to its delay slot.
+
+require {istarget "mips*-*-*"}
+
+standard_testfile
+
+set steps 4
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips1-bal-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips1-bal-delay.c
@@ -0,0 +1,63 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through MIPS I branch-and-link instructions
+   to their delay slot.  */
+
+int
+test_mips1_bal_delay (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  int mone = -1;
+  int zero = 0;
+
+  asm volatile (
+	".macro	b_test op, args:vararg\n\t"
+	"\\op	\\args, 0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"b_test	bltzal, %[mone]\n\t"		/* Taken:     1s  */
+	"b_test	bgezal, %[zero]\n\t"		/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     4s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [mone] "r" (mone), [zero] "r" (zero)
+	: "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips1_bal_delay ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips1-bal-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips1-bal-delay.exp
@@ -0,0 +1,31 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through MIPS I branch-and-link instructions
+# to their delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_mips1_bal_tests
+
+standard_testfile
+
+set steps 4
+foreach flag {-mno-mips16 -minterlink-compressed} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips1-branch-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips1-branch-delay.c
@@ -0,0 +1,67 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be usefu,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through MIPS I branch instructions
+   to their delay slot.  */
+
+int
+test_mips1_branch_delay (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  int mone = -1;
+  int zero = 0;
+  int one = 1;
+
+  asm volatile (
+	".macro	b_test op, args:vararg\n\t"
+	"\\op	\\args, 0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"b_test	beq, %[zero], %[zero]\n\t"	/* Taken:     1s  */
+	"b_test	bne, %[mone], %[one]\n\t"	/* Taken:     1s  */
+	"b_test	bltz, %[mone]\n\t"		/* Taken:     1s  */
+	"b_test	blez, %[zero]\n\t"		/* Taken:     1s  */
+	"b_test	bgez, %[zero]\n\t"		/* Taken:     1s  */
+	"b_test	bgtz, %[one]\n\t"		/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:     8s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [mone] "r" (mone), [zero] "r" (zero), [one] "r" (one));
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips1_branch_delay ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips1-branch-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips1-branch-delay.exp
@@ -0,0 +1,31 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through MIPS I branch instructions
+# to their delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_mips1_branch_tests
+
+standard_testfile
+
+set steps 8
+foreach flag {-mno-mips16 -minterlink-compressed} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips2-branch-delay.c
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips2-branch-delay.c
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test single-stepping through MIPS II branch instructions
+   to their delay slot.  */
+
+int
+test_mips2_branch_delay (void)
+{
+  /* Make 'err' available to the debugger to track the number of single
+     steps executed.  Use `volatile' to prevent the variable from being
+     optimized away.  */
+  volatile int err = -1;
+
+  int mone = -1;
+  int zero = 0;
+  int one = 1;
+
+  asm volatile (
+	".macro	b_test op, args:vararg\n\t"
+	"\\op	\\args, 0f\n"
+	"0:\n\t"
+	" nop\n\t"
+	".endm\n\t"
+
+	".set	push\n\t"
+	".set	noreorder\n\t"
+	".globl	step_start\n\t"
+	".type	step_start, @function\n"
+	"step_start:\n\t"			/* Units: steps.  */
+	"nop\n\t"				/* NOP:       1s  */
+	"b_test	beql, %[zero], %[zero]\n\t"	/* Taken:     1s  */
+	"b_test	bnel, %[mone], %[one]\n\t"	/* Taken:     1s  */
+	"b_test	bltzl, %[mone]\n\t"		/* Taken:     1s  */
+	"b_test	blezl, %[zero]\n\t"		/* Taken:     1s  */
+	"b_test	bgezl, %[zero]\n\t"		/* Taken:     1s  */
+	"b_test	bgtzl, %[one]\n\t"		/* Taken:     1s  */
+	"b_test	bltzall, %[mone]\n\t"		/* Taken:     1s  */
+	"b_test	bgezall, %[zero]\n\t"		/* Taken:     1s  */
+	"nop\n\t"				/* NOP:       1s  */
+	".globl	step_stop\n\t"			/* Total:    10s  */
+	".type	step_stop, @function\n"
+	"step_stop:\n\t"
+	".set pop\n"
+	:
+	: [mone] "r" (mone), [zero] "r" (zero), [one] "r" (one)
+	: "$31");
+
+  return err;
+}
+
+int
+main (void)
+{
+  return test_mips2_branch_delay ();
+}
Index: binutils-gdb/gdb/testsuite/gdb.arch/mips2-branch-delay.exp
===================================================================
--- /dev/null
+++ binutils-gdb/gdb/testsuite/gdb.arch/mips2-branch-delay.exp
@@ -0,0 +1,31 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test single-stepping through MIPS II branch instructions
+# to their delay slot.
+
+require {istarget "mips*-*-*"}
+
+source $srcdir/$subdir/mips-allow.exp.tcl
+
+require allow_mips2_branch_tests
+
+standard_testfile
+
+set steps 10
+foreach flag {-mno-mips16 -mno-micromips -minterlink-compressed} {
+    lappend compile_flags "additional_flags=$flag"
+}
+source $srcdir/$subdir/mips-stepi.exp.tcl
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.