[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