Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH^8] gdb: mips: Add MIPSR6 support
@ 2024-12-13 20:29 Milica Matic
  2025-01-07 20:49 ` Kevin Buettner
  0 siblings, 1 reply; 3+ messages in thread
From: Milica Matic @ 2024-12-13 20:29 UTC (permalink / raw)
  To: gdb-patches
  Cc: Djordje Todorovic, Milos Kalicanin, Simon Marchi, Chao-ying Fu,
	Andrew Burgess, Kevin Buettner, Maciej W. Rozycki

Introduce new instruction encodings from Release 6 of the MIPS
architecture [1]. Support breakpoints and single stepping with
compact branches, forbidden slots, new branch instruction and
new atomic load-store instruction encodings.

Changes from v7: Apply suggestions provided by Kevin Buettner.

[1] "MIPS64 Architecture for Programmers Volume II-A: The MIPS64
    Instruction Set Reference Manual", Document Number: MD00087,
    Revision 6.06, December 15, 2016, Section 3 "The MIPS64
    Instruction Set", pp. 42-530
https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00087-2B-MIPS64BIS-AFP-6.06.pdf
2024-05-15
Andrew Bennett  <andrew.bennett@imgtec.com>
Matthew Fortune  <matthew.fortune@mips.com>
Faraz Shahbazker  <fshahbazker@wavecomp.com>

ChangeLog:

  gdb/mips-tdep.c

    New functions:
      (is_mipsr6_isa)
      (mips32_relative_offset21)
      (mips32_relative_offset26)
      (is_add32bit_overflow)
      (is_add64bit_overflow)
      (mips32_blez_pc)
      (mips32_instruction_is_compact_branch)
      (mips32_insn_at_pc_has_forbidden_slot)
      (is_ll_insn)
      (is_sc_insn)

    Modified functions:
      (mips32_next_pc): Handle r6 compact and fpu coprocessor branches.
                        Move handling of BLEZ, BGTZ opcode into
      (mips32_scan_prologue): Ignore pre-r6 addi encoding on r6.
                              Stop at compact branch also.
      (mips_deal_with_atomic_sequence): Use is_ll_insn/is_sc_insn.
                                        Handle compact branches.
      (mips_about_to_return): Handle jrc and macro jr.
      (mips32_stack_frame_destroyed_p): Likewise.
      (mips32_instruction_has_delay_slot): Don't handle JALX on r6.
                                           Handle compact branches and
                                           coprocessor branches.
      (mips_adjust_breakpoint_address): Skip forbidden slot for
                                        compact branches.

  gdb/testsuite/gdb.arch/mips-64-r6.c and
  gdb/testsuite/gdb.arch/mips-64-r6.exp: tests for the patch.
---
 gdb/mips-tdep.c                       | 665 ++++++++++++++++---
 gdb/testsuite/gdb.arch/mips-64-r6.c   | 915 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/mips-64-r6.exp |  76 +++
 3 files changed, 1550 insertions(+), 106 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/mips-64-r6.c
 create mode 100644 gdb/testsuite/gdb.arch/mips-64-r6.exp

diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index c00efbd02ad..a19a015244d 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -76,6 +76,10 @@ static int mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
 static void mips_print_float_info (struct gdbarch *, struct ui_file *,
 				   const frame_info_ptr &, const char *);
 
+static void
+mips_read_fp_register_single (const frame_info_ptr &frame, int regno,
+			     gdb_byte *rare_buffer);
+
 /* A useful bit in the CP0 status register (MIPS_PS_REGNUM).  */
 /* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip.  */
 #define ST0_FR (1 << 26)
@@ -325,6 +329,17 @@ mips_abi_regsize (struct gdbarch *gdbarch)
     }
 }
 
+/* Return true if the gdbarch is based on MIPS Release 6.  */
+
+static bool
+is_mipsr6_isa (struct gdbarch *gdbarch)
+{
+  const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
+
+  return (info->mach == bfd_mach_mipsisa32r6
+	 || info->mach == bfd_mach_mipsisa64r6);
+}
+
 /* MIPS16/microMIPS function addresses are odd (bit 0 is set).  Here
    are some functions to handle addresses associated with compressed
    code including but not limited to testing, setting, or clearing
@@ -633,7 +648,6 @@ static const char * const mips_linux_reg_names[NUM_MIPS_PROCESSOR_REGS] = {
   "fsr", "fir"
 };
 
-
 /* Return the name of the register corresponding to REGNO.  */
 static const char *
 mips_register_name (struct gdbarch *gdbarch, int regno)
@@ -657,7 +671,7 @@ mips_register_name (struct gdbarch *gdbarch, int regno)
 
   enum mips_abi abi = mips_abi (gdbarch);
 
-  /* Map [gdbarch_num_regs .. 2*gdbarch_num_regs) onto the raw registers, 
+  /* Map [gdbarch_num_regs .. 2*gdbarch_num_regs) onto the raw registers,
      but then don't make the raw register names visible.  This (upper)
      range of user visible register numbers are the pseudo-registers.
 
@@ -1002,7 +1016,7 @@ mips_value_to_register (const frame_info_ptr &frame, int regnum,
       size_t len = type->length ();
       frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
 
-      /* Sign extend values, irrespective of type, that are stored to 
+      /* Sign extend values, irrespective of type, that are stored to
 	 a 64-bit general purpose register.  (32-bit unsigned values
 	 are stored as signed quantities within a 64-bit register.
 	 When performing an operation, in compiled code, that combines
@@ -1538,6 +1552,7 @@ mips_fetch_instruction (struct gdbarch *gdbarch,
 #define b0s11_op(x) ((x) & 0x7ff)
 #define b0s12_imm(x) ((x) & 0xfff)
 #define b0s16_imm(x) ((x) & 0xffff)
+#define b0s21_imm(x) ((x) & 0x1fffff)
 #define b0s26_imm(x) ((x) & 0x3ffffff)
 #define b6s10_ext(x) (((x) >> 6) & 0x3ff)
 #define b11s5_reg(x) (((x) >> 11) & 0x1f)
@@ -1574,6 +1589,24 @@ mips32_relative_offset (ULONGEST inst)
   return ((itype_immediate (inst) ^ 0x8000) - 0x8000) << 2;
 }
 
+/* Calculates pc-relative offset from lower 21 bits of instruction.
+   Used by BEQZC, BNEZC.  */
+
+static LONGEST
+mips32_relative_offset21 (ULONGEST insn)
+{
+  return ((b0s21_imm (insn) ^ 0x100000) - 0x100000) << 2;
+}
+
+/* Calculates pc-relative offset from lower 26 bits of an instruction.
+   Used by BC, BALC.  */
+
+static LONGEST
+mips32_relative_offset26 (ULONGEST insn)
+{
+  return ((b0s26_imm (insn) ^ 0x2000000) - 0x2000000) << 2;
+}
+
 /* Determine the address of the next instruction executed after the INST
    floating condition branch instruction at PC.  COUNT specifies the
    number of the floating condition bits tested by the branch.  */
@@ -1632,6 +1665,82 @@ is_octeon_bbit_op (int op, struct gdbarch *gdbarch)
   return 0;
 }
 
+/* Detects whether overflow occurs when adding two 32-bit integers.  */
+
+static int
+is_add32bit_overflow (int32_t a, int32_t b)
+{
+  int32_t r = (uint32_t) a + (uint32_t) b;
+  return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0);
+}
+
+/* Helper function for BOVC/BNVC instructions which are introduced in
+   MIPS Release 6.
+
+   BOVC performs a signed 32-bit addition of two registers. BOVC discards the
+   sum, but detects signed 32-bit integer overflow of the sum (and the inputs,
+   in MIPS64), and branches if such overflow is detected.
+
+   BNVC does the opposite, i.e. branches if such overflow is not detected.  */
+
+static int
+is_add64bit_overflow (int64_t a, int64_t b)
+{
+  if (a != (int32_t)a)
+    return 1;
+  if (b != (int32_t)b)
+    return 1;
+  return is_add32bit_overflow ((int32_t)a, (int32_t)b);
+}
+
+/* Calculate address of next instruction after BLEZ.  */
+
+static CORE_ADDR
+mips32_blez_pc (struct gdbarch *gdbarch, struct regcache *regcache,
+	       ULONGEST inst, CORE_ADDR pc, int invert)
+{
+  int rs = itype_rs (inst);
+  int rt = itype_rt (inst);
+  LONGEST val_rs = regcache_raw_get_signed (regcache, rs);
+  LONGEST val_rt = regcache_raw_get_signed (regcache, rt);
+  ULONGEST uval_rs = regcache_raw_get_unsigned (regcache, rs);
+  ULONGEST uval_rt = regcache_raw_get_unsigned (regcache, rt);
+  int taken = 0;
+  int delay_slot_size = 4;
+
+  /* BLEZ, BLEZL, BGTZ, BGTZL  */
+  if (rt == 0)
+    taken = (val_rs <= 0);
+  else if (is_mipsr6_isa (gdbarch))
+    {
+      /* BLEZALC, BGTZALC  */
+      if (rs == 0 && rt != 0)
+       taken = (val_rt <= 0);
+      /* BGEZALC, BLTZALC  */
+      else if (rs == rt && rt != 0)
+       taken = (val_rt >= 0);
+      /* BGEUC, BLTUC  */
+      else if (rs != rt && rs != 0 && rt != 0)
+       taken = (uval_rs >= uval_rt);
+
+      /* Step through the forbidden slot to avoid repeated exceptions we do
+	not currently have access to the BD bit when hitting a breakpoint
+	and therefore cannot tell if the breakpoint hit on the branch or the
+	forbidden slot.  */
+      /* delay_slot_size = 0;  */
+    }
+
+  if (invert)
+    taken = !taken;
+
+  /* Calculate branch target.  */
+  if (taken)
+    pc += mips32_relative_offset (inst);
+  else
+    pc += delay_slot_size;
+
+  return pc;
+}
 
 /* Determine where to set a single step breakpoint while considering
    branch prediction.  */
@@ -1642,58 +1751,66 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
   struct gdbarch *gdbarch = regcache->arch ();
   unsigned long inst;
   int op;
+  int mips64bitreg = 0;
+
+  if (mips_isa_regsize (gdbarch) == 8)
+    mips64bitreg = 1;
+
   inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL);
   op = itype_op (inst);
-  if ((inst & 0xe0000000) != 0)		/* Not a special, jump or branch
-					   instruction.  */
+  if ((inst & 0xe0000000) != 0) /* Not a special, jump or branch instruction. */
+    {
+      if (op >> 2 == 5 && ((op & 0x02) == 0 || itype_rt (inst) == 0))
+    /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
     {
-      if (op >> 2 == 5)
-	/* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
+      switch (op & 0x03)
 	{
-	  switch (op & 0x03)
-	    {
-	    case 0:		/* BEQL */
-	      goto equal_branch;
-	    case 1:		/* BNEL */
-	      goto neq_branch;
-	    case 2:		/* BLEZL */
-	      goto less_branch;
-	    case 3:		/* BGTZL */
-	      goto greater_branch;
-	    default:
-	      pc += 4;
-	    }
+	case 0:		/* BEQL */
+	  goto equal_branch;
+	case 1:		/* BNEL */
+	  goto neq_branch;
+	case 2:		/* BLEZL */
+	  goto lez_branch;
+	case 3:		/* BGTZL */
+	  goto greater_branch;
+	default:
+	  pc += 4;
 	}
+    }
       else if (op == 17 && itype_rs (inst) == 8)
-	/* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
-	pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
-      else if (op == 17 && itype_rs (inst) == 9
+    /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
+    pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
+      else if (!is_mipsr6_isa (gdbarch)
+	      && op == 17
+	      && itype_rs (inst) == 9
 	       && (itype_rt (inst) & 2) == 0)
 	/* BC1ANY2F, BC1ANY2T: 010001 01001 xxx0x */
 	pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 2);
-      else if (op == 17 && itype_rs (inst) == 10
-	       && (itype_rt (inst) & 2) == 0)
+      else if (!is_mipsr6_isa (gdbarch)
+	      && op == 17
+	      && itype_rs (inst) == 10
+	   && (itype_rt (inst) & 2) == 0)
 	/* BC1ANY4F, BC1ANY4T: 010001 01010 xxx0x */
-	pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 4);
-      else if (op == 29)
-	/* JALX: 011101 */
-	/* The new PC will be alternate mode.  */
-	{
-	  unsigned long reg;
+    pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 4);
+      else if (!is_mipsr6_isa (gdbarch) && op == 29)
+    /* JALX: 011101 */
+    /* The new PC will be alternate mode.  */
+    {
+      unsigned long reg;
 
-	  reg = jtype_target (inst) << 2;
-	  /* Add 1 to indicate 16-bit mode -- invert ISA mode.  */
-	  pc = ((pc + 4) & ~(CORE_ADDR) 0x0fffffff) + reg + 1;
+      reg = jtype_target (inst) << 2;
+      /* Add 1 to indicate 16-bit mode -- invert ISA mode.  */
+      pc = ((pc + 4) & ~(CORE_ADDR) 0x0fffffff) + reg + 1;
 	}
       else if (is_octeon_bbit_op (op, gdbarch))
-	{
-	  int bit, branch_if;
+    {
+      int bit, branch_if;
 
-	  branch_if = op == 58 || op == 62;
-	  bit = itype_rt (inst);
+      branch_if = op == 58 || op == 62;
+      bit = itype_rt (inst);
 
-	  /* Take into account the *32 instructions.  */
-	  if (op == 54 || op == 62)
+      /* Take into account the *32 instructions.  */
+      if (op == 54 || op == 62)
 	    bit += 32;
 
 	  if (((regcache_raw_get_signed (regcache,
@@ -1703,7 +1820,128 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
 	  else
 	    pc += 8;        /* After the delay slot.  */
 	}
-
+      else if (is_mipsr6_isa (gdbarch))
+       {
+	 /* BOVC, BEQZALC, BEQC and BNVC, BNEZALC, BNEC  */
+	 if (op == 8 || op == 24)
+	   {
+	     int rs = rtype_rs (inst);
+	     int rt = rtype_rt (inst);
+	     LONGEST val_rs = regcache_raw_get_signed (regcache, rs);
+	     LONGEST val_rt = regcache_raw_get_signed (regcache, rt);
+	     int taken = 0;
+	     /* BOVC (BNVC)  */
+	     if (rs >= rt)
+	       {
+		 if (mips64bitreg == 1)
+		   taken = is_add64bit_overflow (val_rs, val_rt);
+		 else
+		   taken = is_add32bit_overflow (val_rs, val_rt);
+	       }
+	     /* BEQZALC (BNEZALC)  */
+	     else if (rs < rt && rs == 0)
+	       taken = (val_rt == 0);
+	     /* BEQC (BNEC)  */
+	     else
+	       taken = (val_rs == val_rt);
+
+	     /* BNVC, BNEZALC, BNEC  */
+	     if (op == 24)
+	       taken = !taken;
+
+	     if (taken)
+	       pc += mips32_relative_offset (inst) + 4;
+	     else
+	       /* Step through the forbidden slot to avoid repeated exceptions
+		  we do not currently have access to the BD bit when hitting a
+		  breakpoint and therefore cannot tell if the breakpoint
+		  hit on the branch or the forbidden slot.  */
+	       pc += 8;
+	   }
+	 /* BC1EQZ, BC1NEZ  */
+	 else if (op == 17 && (itype_rs (inst) == 9 || itype_rs (inst) == 13))
+	   {
+	     gdb_byte status;
+	     gdb_byte true_val = 0;
+	     unsigned int fp = (gdbarch_num_regs (gdbarch)
+				+ mips_regnum (gdbarch)->fp0
+				+ itype_rt (inst));
+	     struct frame_info_ptr frame = get_current_frame ();
+	     gdb_byte *raw_buffer = (gdb_byte *) alloca (sizeof (gdb_byte) * 4);
+	     mips_read_fp_register_single (frame, fp, raw_buffer);
+
+	     if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+	       status = *(raw_buffer + 3);
+	     else
+	       status = *(raw_buffer);
+
+	     if (itype_rs (inst) == 13)
+	       true_val = 1;
+
+	     if ((status & 0x1) == true_val)
+	       pc += mips32_relative_offset (inst) + 4;
+	     else
+	       pc += 8;
+	   }
+	 else if (op == 22 || op == 23)
+	 /* BLEZC, BGEZC, BGEC, BGTZC, BLTZC, BLTC  */
+	   {
+	     int rs = rtype_rs (inst);
+	     int rt = rtype_rt (inst);
+	     LONGEST val_rs = regcache_raw_get_signed (regcache, rs);
+	     LONGEST val_rt = regcache_raw_get_signed (regcache, rt);
+	     int taken = 0;
+	     /* The R5 rt == 0 case is handled above so we treat it as
+		an unknown instruction here for future ISA usage.  */
+	     if (rs == 0 && rt != 0)
+	       taken = (val_rt <= 0);
+	     else if (rs == rt && rt != 0)
+	       taken = (val_rt >= 0);
+	     else if (rs != rt && rs != 0 && rt != 0)
+	       taken = (val_rs >= val_rt);
+
+	     if (op == 23)
+	       taken = !taken;
+
+	     if (taken)
+	       pc += mips32_relative_offset (inst) + 4;
+	     else
+	       /* Step through the forbidden slot to avoid repeated exceptions
+		  we do not currently have access to the BD bit when hitting a
+		  breakpoint and therefore cannot tell if the breakpoint
+		  hit on the branch or the forbidden slot.  */
+	       pc += 8;
+	   }
+	 else if (op == 50 || op == 58)
+	 /* BC, BALC  */
+	   pc += mips32_relative_offset26 (inst) + 4;
+	 else if ((op == 54 || op == 62)
+		  && rtype_rs (inst) == 0)
+	 /* JIC, JIALC  */
+	   {
+	     pc = regcache_raw_get_signed (regcache, itype_rt (inst));
+	     pc += (itype_immediate (inst) ^ 0x8000) - 0x8000;
+	   }
+	 else if (op == 54 || op == 62)
+	 /* BEQZC, BNEZC  */
+	   {
+	     int rs = itype_rs (inst);
+	     LONGEST rs_val = regcache_raw_get_signed (regcache, rs);
+	     int taken = (rs_val == 0);
+	     if (op == 62)
+	       taken = !taken;
+	     if (taken)
+	       pc += mips32_relative_offset21 (inst) + 4;
+	     else
+	       /* Step through the forbidden slot to avoid repeated exceptions
+		  we do not currently have access to the BD bit when hitting a
+		  breakpoint and therefore cannot tell if the breakpoint
+		  hit on the branch or the forbidden slot.  */
+	       pc += 8;
+	   }
+	 else
+	   pc += 4;            /* Not a branch, next instruction is easy.  */
+       }
       else
 	pc += 4;		/* Not a branch, next instruction is easy.  */
     }
@@ -1747,7 +1985,6 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
 	      case 2:		/* BLTZL */
 	      case 16:		/* BLTZAL */
 	      case 18:		/* BLTZALL */
-	      less_branch:
 		if (regcache_raw_get_signed (regcache, itype_rs (inst)) < 0)
 		  pc += mips32_relative_offset (inst) + 4;
 		else
@@ -1763,22 +2000,38 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
 		  pc += 8;	/* after the delay slot */
 		break;
 	      case 0x1c:	/* BPOSGE32 */
+	     case 0x1d:        /* BPOSGE32C  */
 	      case 0x1e:	/* BPOSGE64 */
 		pc += 4;
 		if (itype_rs (inst) == 0)
 		  {
 		    unsigned int pos = (op & 2) ? 64 : 32;
 		    int dspctl = mips_regnum (gdbarch)->dspctl;
+		   int delay_slot_size = 4;
 
 		    if (dspctl == -1)
 		      /* No way to handle; it'll most likely trap anyway.  */
 		      break;
 
+		   /* BPOSGE32C  */
+		   if (op == 0x1d)
+		     {
+		       if (!is_mipsr6_isa (gdbarch))
+			 break;
+
+		       /* Step through the forbidden slot to avoid repeated
+			  exceptions we do not currently have access to the BD
+			  bit when hitting a breakpoint and therefore cannot
+			  tell if the breakpoint hit on the branch or the
+			  forbidden slot.  */
+		       /* delay_slot_size = 0;  */
+		     }
+
 		    if ((regcache_raw_get_unsigned (regcache,
 						    dspctl) & 0x7f) >= pos)
 		      pc += mips32_relative_offset (inst);
 		    else
-		      pc += 4;
+		     pc += delay_slot_size;
 		  }
 		break;
 		/* All of the other instructions in the REGIMM category */
@@ -1812,19 +2065,14 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
 	  else
 	    pc += 8;
 	  break;
-	case 6:		/* BLEZ, BLEZL */
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) <= 0)
-	    pc += mips32_relative_offset (inst) + 4;
-	  else
-	    pc += 8;
+       case 6:         /* BLEZ, BLEZL, BLEZALC, BGEZALC, BGEUC  */
+       lez_branch:
+	 pc = mips32_blez_pc (gdbarch, regcache, inst, pc + 4, 0);
 	  break;
 	case 7:
 	default:
-	greater_branch:	/* BGTZ, BGTZL */
-	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) > 0)
-	    pc += mips32_relative_offset (inst) + 4;
-	  else
-	    pc += 8;
+       greater_branch: /* BGTZ, BGTZL, BGTZALC, BLTZALC, BLTUC  */
+	 pc = mips32_blez_pc (gdbarch, regcache, inst, pc + 4, 1);
 	  break;
 	}			/* switch */
     }				/* else */
@@ -2156,7 +2404,6 @@ struct upk_mips16
   unsigned int regy;
 };
 
-
 /* The EXT-I, EXT-ri nad EXT-I8 instructions all have the same format
    for the bits which make up the immediate extension.  */
 
@@ -2264,7 +2511,6 @@ unpack_mips16 (struct gdbarch *gdbarch, CORE_ADDR pc,
   upk->regy = regy;
 }
 
-
 /* Calculate the destination of a branch whose 16-bit opcode word is at PC,
    and having a signed 16-bit OFFSET.  */
 
@@ -2447,6 +2693,72 @@ micromips_instruction_is_compact_branch (unsigned short insn)
     }
 }
 
+/* Return non-zero if the MIPS instruction INSN is a compact branch
+   or jump.  A value of 1 indicates an unconditional compact branch
+   and a value of 2 indicates a conditional compact branch.  */
+
+static int
+mips32_instruction_is_compact_branch (struct gdbarch *gdbarch, ULONGEST insn)
+{
+  switch (itype_op (insn))
+    {
+    /* BC  */
+    case 50:
+    /* BALC  */
+    case 58:
+      if (is_mipsr6_isa (gdbarch))
+       return 1;
+      break;
+    /* BOVC, BEQZALC, BEQC  */
+    case 8:
+    /* BNVC, BNEZALC, BNEC  */
+    case 24:
+      if (is_mipsr6_isa (gdbarch))
+       return 2;
+      break;
+    /* BEQZC, JIC  */
+    case 54:
+    /* BNEZC, JIALC  */
+    case 62:
+      if (is_mipsr6_isa (gdbarch))
+       /* JIC, JIALC are unconditional  */
+       return (itype_rs (insn) == 0) ? 1 : 2;
+      break;
+    /* BLEZC, BGEZC, BGEC  */
+    case 22:
+    /* BGTZC, BLTZC, BLTC  */
+    case 23:
+    /* BLEZALC, BGEZALC, BGEUC  */
+    case 6:
+    /* BGTZALC, BLTZALC, BLTUC  */
+    case 7:
+      if (is_mipsr6_isa (gdbarch)
+	 && itype_rt (insn) != 0)
+       return 2;
+      break;
+    /* BPOSGE32C  */
+    case 1:
+      if (is_mipsr6_isa (gdbarch)
+	 && itype_rt (insn) == 0x1d && itype_rs (insn) == 0)
+       return 2;
+    }
+  return 0;
+}
+
+/* Return true if a standard MIPS instruction at ADDR has a branch
+   forbidden slot (i.e. it is a conditional compact branch instruction).  */
+
+static bool
+mips32_insn_at_pc_has_forbidden_slot (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  int status;
+  ULONGEST insn = mips_fetch_instruction (gdbarch, ISA_MIPS, addr, &status);
+  if (status)
+    return false;
+
+  return mips32_instruction_is_compact_branch (gdbarch, insn) == 2;
+}
+
 struct mips_frame_cache
 {
   CORE_ADDR base;
@@ -2479,7 +2791,6 @@ set_reg_offset (struct gdbarch *gdbarch, struct mips_frame_cache *this_cache,
     }
 }
 
-
 /* Fetch the immediate value from a MIPS16 instruction.
    If the previous instruction was an EXTEND, use it to extend
    the upper bits of the immediate value.  This is a helper function
@@ -2514,7 +2825,6 @@ mips16_get_imm (unsigned short prev_inst,	/* previous instruction */
     }
 }
 
-
 /* Analyze the function prologue from START_PC to LIMIT_PC. Builds
    the associated FRAME_CACHE if not null.
    Return the address of the first instruction past the prologue.  */
@@ -2885,7 +3195,7 @@ mips_insn16_frame_cache (const frame_info_ptr &this_frame, void **this_cache)
     mips16_scan_prologue (gdbarch, start_addr, pc, this_frame,
 			  (struct mips_frame_cache *) *this_cache);
   }
-  
+
   /* gdbarch_sp_regnum contains the value and not the address.  */
   cache->saved_regs[gdbarch_num_regs (gdbarch)
 		    + MIPS_SP_REGNUM].set_value (cache->base);
@@ -3422,7 +3732,7 @@ reset_saved_regs (struct gdbarch *gdbarch, struct mips_frame_cache *this_cache)
 }
 
 /* Analyze the function prologue from START_PC to LIMIT_PC.  Builds
-   the associated FRAME_CACHE if not null.  
+   the associated FRAME_CACHE if not null.
    Return the address of the first instruction past the prologue.  */
 
 static CORE_ADDR
@@ -3490,7 +3800,8 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
       reg = high_word & 0x1f;
 
       if (high_word == 0x27bd		/* addiu $sp,$sp,-i */
-	  || high_word == 0x23bd	/* addi $sp,$sp,-i */
+	 || (high_word == 0x23bd       /* addi $sp,$sp,-i  */
+	     && !is_mipsr6_isa (gdbarch))
 	  || high_word == 0x67bd)	/* daddiu $sp,$sp,-i */
 	{
 	  if (offset < 0)		/* Negative stack adjustment?  */
@@ -3628,7 +3939,9 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
 
       /* A jump or branch, or enough non-prologue insns seen?  If so,
 	 then we must have reached the end of the prologue by now.  */
-      if (prev_delay_slot || non_prologue_insns > 1)
+      if (prev_delay_slot
+	 || non_prologue_insns > 1
+	 || mips32_instruction_is_compact_branch (gdbarch, inst))
 	break;
 
       prev_non_prologue_insn = this_non_prologue_insn;
@@ -3638,7 +3951,7 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
 
   if (this_cache != NULL)
     {
-      this_cache->base = 
+      this_cache->base =
 	(get_frame_register_signed (this_frame,
 				    gdbarch_num_regs (gdbarch) + frame_reg)
 	 + frame_offset);
@@ -3657,7 +3970,7 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
      its address instead.  */
   end_prologue_addr
     = prev_non_prologue_insn || prev_delay_slot ? prev_pc : cur_pc;
-     
+
   /* In a frameless function, we might have incorrectly
      skipped some load immediate instructions.  Undo the skipping
      if the load immediate was not followed by a stack adjustment.  */
@@ -3701,7 +4014,7 @@ mips_insn32_frame_cache (const frame_info_ptr &this_frame, void **this_cache)
     mips32_scan_prologue (gdbarch, start_addr, pc, this_frame,
 			  (struct mips_frame_cache *) *this_cache);
   }
-  
+
   /* gdbarch_sp_regnum contains the value and not the address.  */
   cache->saved_regs[gdbarch_num_regs (gdbarch)
 		    + MIPS_SP_REGNUM].set_value (cache->base);
@@ -3921,10 +4234,9 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
     return addr;
 }
 
-
 /* Checks for an atomic sequence of instructions beginning with a LL/LLD
    instruction and ending with a SC/SCD instruction.  If such a sequence
-   is found, attempt to step through it.  A breakpoint is placed at the end of 
+   is found, attempt to step through it.  A breakpoint is placed at the end of
    the sequence.  */
 
 /* Instructions used during single-stepping of atomic sequences, standard
@@ -3933,6 +4245,70 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
 #define LLD_OPCODE 0x34
 #define SC_OPCODE 0x38
 #define SCD_OPCODE 0x3c
+#define LLSC_R6_OPCODE 0x1f
+#define LL_R6_FUNCT 0x36
+#define LLE_FUNCT 0x2e
+#define LLD_R6_FUNCT 0x37
+#define SC_R6_FUNCT 0x26
+#define SCE_FUNCT 0x1e
+#define SCD_R6_FUNCT 0x27
+
+/* Determine whether instruction 'insn' is of 'load linked X' type.
+   LL/SC instructions provide primitives to implement atomic
+   read-modify-write operations for synchronizable memory locations.  */
+
+static bool
+is_ll_insn (struct gdbarch *gdbarch, ULONGEST insn)
+{
+  if (itype_op (insn) == LL_OPCODE
+      || itype_op (insn) == LLD_OPCODE)
+    return true;
+
+  if (rtype_op (insn) == LLSC_R6_OPCODE
+      && rtype_funct (insn) == LLE_FUNCT
+      && (insn & 0x40) == 0)
+    return true;
+
+  /* Handle LL and LLP varieties.  */
+  if (is_mipsr6_isa (gdbarch)
+      && rtype_op (insn) == LLSC_R6_OPCODE
+      && (rtype_funct (insn) == LL_R6_FUNCT
+	 || rtype_funct (insn) == LLD_R6_FUNCT
+	 || rtype_funct (insn) == LLE_FUNCT))
+    return true;
+
+  return false;
+}
+
+/* Determine whether instruction 'insn' is of 'store conditional X' type.
+   SC instructions and varieties perform completion of read-modify-write
+   atomic sequence.  */
+
+static bool
+is_sc_insn (struct gdbarch *gdbarch, ULONGEST insn)
+{
+  if (itype_op (insn) == SC_OPCODE
+      || itype_op (insn) == SCD_OPCODE)
+    return true;
+
+  if (rtype_op (insn) == LLSC_R6_OPCODE
+      && rtype_funct (insn) == SCE_FUNCT
+      && (insn & 0x40) == 0)
+    return true;
+
+  /* Handle SC and SCP varieties.  */
+  if (is_mipsr6_isa (gdbarch)
+      && rtype_op (insn) == LLSC_R6_OPCODE
+      && (rtype_funct (insn) == SC_R6_FUNCT
+	 || rtype_funct (insn) == SCD_R6_FUNCT
+	 || rtype_funct (insn) == SCE_FUNCT))
+    return true;
+
+  return false;
+}
+
+/* Handle mips atomic sequence which starts with LL/LLD and ends with
+   SC/SCD instruction.  */
 
 static std::vector<CORE_ADDR>
 mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
@@ -3943,15 +4319,16 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
   ULONGEST insn;
   int insn_count;
   int index;
-  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */  
+  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
   const int atomic_sequence_length = 16; /* Instruction sequence length.  */
+  bool is_mipsr6 = is_mipsr6_isa (gdbarch);
 
   insn = mips_fetch_instruction (gdbarch, ISA_MIPS, loc, NULL);
   /* Assume all atomic sequences start with a ll/lld instruction.  */
-  if (itype_op (insn) != LL_OPCODE && itype_op (insn) != LLD_OPCODE)
+  if (!is_ll_insn (gdbarch, insn))
     return {};
 
-  /* Assume that no atomic sequence is longer than "atomic_sequence_length" 
+  /* Assume that no atomic sequence is longer than "atomic_sequence_length"
      instructions.  */
   for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
     {
@@ -3978,28 +4355,72 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
 	  return {}; /* fallback to the standard single-step code.  */
 	case 4: /* BEQ */
 	case 5: /* BNE */
-	case 6: /* BLEZ */
-	case 7: /* BGTZ */
 	case 20: /* BEQL */
 	case 21: /* BNEL */
-	case 22: /* BLEZL */
-	case 23: /* BGTTL */
+       case 22: /* BLEZL (BLEZC, BGEZC, BGEC)  */
+       case 23: /* BGTZL (BGTZC, BLTZC, BLTC)  */
 	  is_branch = 1;
 	  break;
+       case 6: /* BLEZ (BLEZALC, BGEZALC, BGEUC)  */
+       case 7: /* BGTZ (BGTZALC, BLTZALC, BLTUC)  */
+	 if (is_mipsr6)
+	   {
+	     /* BLEZALC, BGTZALC  */
+	     if (itype_rs (insn) == 0 && itype_rt (insn) != 0)
+	       return {}; /* fallback to the standard single-step code.  */
+	     /* BGEZALC, BLTZALC  */
+	     else if (itype_rs (insn) == itype_rt (insn)
+		      && itype_rt (insn) != 0)
+	       return {}; /* fallback to the standard single-step code.  */
+	   }
+	 is_branch = 1;
+	 break;
+       case 8: /* BOVC, BEQZALC, BEQC  */
+       case 24: /* BNVC, BNEZALC, BNEC  */
+	 if (is_mipsr6)
+	   is_branch = 1;
+	 break;
+       case 50: /* BC  */
+       case 58: /* BALC  */
+	 if (is_mipsr6)
+	   return {}; /* fallback to the standard single-step code.  */
+	 break;
+       case 54: /* BEQZC, JIC  */
+       case 62: /* BNEZC, JIALC  */
+	 if (is_mipsr6)
+	   {
+	     if (itype_rs (insn) == 0) /* JIC, JIALC  */
+	       return {}; /* fallback to the standard single-step code.  */
+	     else
+	       is_branch = 2; /* Marker for branches with a 21-bit offset.  */
+	   }
+	 break;
 	case 17: /* COP1 */
-	  is_branch = ((itype_rs (insn) == 9 || itype_rs (insn) == 10)
-		       && (itype_rt (insn) & 0x2) == 0);
-	  if (is_branch) /* BC1ANY2F, BC1ANY2T, BC1ANY4F, BC1ANY4T */
+	 is_branch = ((!is_mipsr6
+		       /* BC1ANY2F, BC1ANY2T, BC1ANY4F, BC1ANY4T  */
+		       && (itype_rs (insn) == 9 || itype_rs (insn) == 10)
+		       && (itype_rt (insn) & 0x2) == 0)
+		      /* BZ.df:  010001 110xx  */
+		      || (itype_rs (insn) & 0x18) == 0x18);
+	 if (is_branch != 0)
 	    break;
 	  [[fallthrough]];
 	case 18: /* COP2 */
 	case 19: /* COP3 */
-	  is_branch = (itype_rs (insn) == 8); /* BCzF, BCzFL, BCzT, BCzTL */
+	 /* BCzF, BCzFL, BCzT, BCzTL, BC*EQZ, BC*NEZ  */
+	 is_branch = ((itype_rs (insn) == 8)
+		      || (is_mipsr6
+			  && (itype_rs (insn) == 9
+			      || itype_rs (insn) == 13)));
 	  break;
 	}
-      if (is_branch)
+      if (is_branch != 0)
 	{
-	  branch_bp = loc + mips32_relative_offset (insn) + 4;
+	 /* Is this a special PC21_S2 branch?  */
+	 if (is_branch == 2)
+	   branch_bp = loc + mips32_relative_offset21 (insn) + 4;
+	 else
+	   branch_bp = loc + mips32_relative_offset (insn) + 4;
 	  if (last_breakpoint >= 1)
 	    return {}; /* More than one branch found, fallback to the
 			  standard single-step code.  */
@@ -4007,12 +4428,12 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
 	  last_breakpoint++;
 	}
 
-      if (itype_op (insn) == SC_OPCODE || itype_op (insn) == SCD_OPCODE)
+      if (is_sc_insn (gdbarch, insn))
 	break;
     }
 
   /* Assume that the atomic sequence ends with a sc/scd instruction.  */
-  if (itype_op (insn) != SC_OPCODE && itype_op (insn) != SCD_OPCODE)
+  if (!is_sc_insn (gdbarch, insn))
     return {};
 
   loc += MIPS_INSN32_SIZE;
@@ -4161,7 +4582,7 @@ micromips_deal_with_atomic_sequence (struct gdbarch *gdbarch,
 	    }
 	  break;
 	}
-      if (is_branch)
+      if (is_branch != 0)
 	{
 	  if (last_breakpoint >= 1)
 	    return {}; /* More than one branch found, fallback to the
@@ -4237,10 +4658,15 @@ mips_about_to_return (struct gdbarch *gdbarch, CORE_ADDR pc)
   gdb_assert (mips_pc_is_mips (pc));
 
   insn = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL);
-  hint = 0x7c0;
-  return (insn & ~hint) == 0x3e00008;			/* jr(.hb) $ra */
-}
+  /* Mask the hint and the jalr/jr bit.  */
+  hint = 0x7c1;
+
+  if (is_mipsr6_isa (gdbarch) && insn == 0xd81f0000) /* jrc $31  */
+    return 1;
 
+  /* jr(.hb) $ra and "jalr(.hb) $ra"  */
+  return ((insn & ~hint) == 0x3e00008);
+}
 
 /* This fencepost looks highly suspicious to me.  Removing it also
    seems suspicious as it could affect remote debugging across serial
@@ -4814,7 +5240,7 @@ mips_eabi_return_value (struct gdbarch *gdbarch, struct value *function,
     {
       if (type->code () == TYPE_CODE_FLT)
 	fp_return_type = 1;
-      /* Structs with a single field of float type 
+      /* Structs with a single field of float type
 	 are returned in a floating point register.  */
       if ((type->code () == TYPE_CODE_STRUCT
 	   || type->code () == TYPE_CODE_UNION)
@@ -4827,7 +5253,7 @@ mips_eabi_return_value (struct gdbarch *gdbarch, struct value *function,
 	}
     }
 
-  if (fp_return_type)      
+  if (fp_return_type)
     {
       /* A floating-point value belongs in the least significant part
 	 of FP0/FP1.  */
@@ -4835,7 +5261,7 @@ mips_eabi_return_value (struct gdbarch *gdbarch, struct value *function,
 	gdb_printf (gdb_stderr, "Return float in $fp0\n");
       regnum = mips_regnum (gdbarch)->fp0;
     }
-  else 
+  else
     {
       /* An integer value goes in V0/V1.  */
       if (mips_debug)
@@ -4858,7 +5284,6 @@ mips_eabi_return_value (struct gdbarch *gdbarch, struct value *function,
   return RETURN_VALUE_REGISTER_CONVENTION;
 }
 
-
 /* N32/N64 ABI stuff.  */
 
 /* Search for a naturally aligned double at OFFSET inside a struct
@@ -6519,7 +6944,6 @@ print_fp_register_row (struct ui_file *file, const frame_info_ptr &frame,
   return regnum + 1;
 }
 
-
 /* Print a row's worth of GP (int) registers, with name labels above.  */
 
 static int
@@ -6755,7 +7179,9 @@ mips32_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
 
 	  if (high_word != 0x27bd	/* addiu $sp,$sp,offset */
 	      && high_word != 0x67bd	/* daddiu $sp,$sp,offset */
-	      && inst != 0x03e00008	/* jr $ra */
+	     && (inst & ~0x1) != 0x03e00008 /* jr $31 or jalr $0, $31  */
+	     && (!is_mipsr6_isa (gdbarch)
+		 || inst != 0xd81f0000) /* jrc $31  */
 	      && inst != 0x00000000)	/* nop */
 	    return 0;
 	}
@@ -6958,7 +7384,6 @@ show_mipsfpu_command (const char *args, int from_tty)
       ("The MIPS floating-point coprocessor is assumed to be %s\n", fpu);
 }
 
-
 static void
 set_mipsfpu_single_command (const char *args, int from_tty)
 {
@@ -7134,22 +7559,31 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
   int op;
   int rs;
   int rt;
+  bool is_mipsr6 = is_mipsr6_isa (gdbarch);
 
   op = itype_op (inst);
   if ((inst & 0xe0000000) != 0)
     {
       rs = itype_rs (inst);
       rt = itype_rt (inst);
-      return (is_octeon_bbit_op (op, gdbarch) 
-	      || op >> 2 == 5	/* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx  */
-	      || op == 29	/* JALX: bits 011101  */
+      return (is_octeon_bbit_op (op, gdbarch)
+	     || (op >> 1 == 10) /* BEQL, BNEL: bits 01010x  */
+	     || (op >> 1 == 11 && rt == 0) /* BLEZL, BGTZL: bits 01011x  */
+	     || (!is_mipsr6 && op == 29)       /* JALX: bits 011101  */
 	      || (op == 17
 		  && (rs == 8
 				/* BC1F, BC1FL, BC1T, BC1TL: 010001 01000  */
-		      || (rs == 9 && (rt & 0x2) == 0)
+		     || (!is_mipsr6 && rs == 9 && (rt & 0x2) == 0)
 				/* BC1ANY2F, BC1ANY2T: bits 010001 01001  */
-		      || (rs == 10 && (rt & 0x2) == 0))));
+		     || (!is_mipsr6 && rs == 10 && (rt & 0x2) == 0)))
 				/* BC1ANY4F, BC1ANY4T: bits 010001 01010  */
+	     || (is_mipsr6
+		 && ((op == 17
+		      && (rs == 9  /* BC1EQZ: 010001 01001  */
+			  || rs == 13))  /* BC1NEZ: 010001 01101  */
+		     || (op == 18
+			 && (rs == 9  /* BC2EQZ: 010010 01001  */
+			     || rs == 13)))));  /* BC2NEZ: 010010 01101  */
     }
   else
     switch (op & 0x07)		/* extract bits 28,27,26  */
@@ -7168,7 +7602,11 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
 		|| ((rt & 0x1e) == 0x1c && rs == 0));
 				/* BPOSGE32, BPOSGE64: bits 1110x  */
 	break;			/* end REGIMM  */
-      default:			/* J, JAL, BEQ, BNE, BLEZ, BGTZ  */
+       case 6:                 /* BLEZ  */
+       case 7:                 /* BGTZ  */
+	return (itype_rt (inst) == 0);
+	break;
+      default:                 /* J, JAL, BEQ, BNE  */
 	return 1;
 	break;
       }
@@ -7346,7 +7784,7 @@ mips_segment_boundary (CORE_ADDR bpaddr)
 }
 
 /* Move the breakpoint at BPADDR out of any branch delay slot by shifting
-   it backwards if necessary.  Return the address of the new location.  */
+   it backwards if necessary. Return the address of the new location.  */
 
 static CORE_ADDR
 mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
@@ -7380,7 +7818,18 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
 
      So, we'll use the second solution.  To do this we need to know if
      the instruction we're trying to set the breakpoint on is in the
-     branch delay slot.  */
+     branch delay slot.
+
+     A similar problem occurs for breakpoints on forbidden slots where
+     the trap will be reported for the branch with the BD bit set.
+     In this case it would be ideal to recover using solution 1 from
+     above as there is no problem with the branch being skipped
+     (since the forbidden slot only exists on not-taken branches).
+     However, the BD bit is not available in all scenarios currently
+     so instead we move the breakpoint on to the next instruction.
+     This means that it is not possible to stop on an instruction
+     that can be in a forbidden slot even if that instruction is
+     jumped to directly.  */
 
   boundary = mips_segment_boundary (bpaddr);
 
@@ -7402,6 +7851,12 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
       prev_addr = bpaddr - 4;
       if (mips32_insn_at_pc_has_delay_slot (gdbarch, prev_addr))
 	bpaddr = prev_addr;
+      /* If the previous instruction has a forbidden slot, we have to
+	move the breakpoint to the following instruction to prevent
+	breakpoints in forbidden slots being reported as unknown
+	traps.  */
+      else if (mips32_insn_at_pc_has_forbidden_slot (gdbarch, prev_addr))
+       bpaddr += 4;
     }
   else
     {
@@ -7916,7 +8371,6 @@ mips_stab_reg_to_regnum (struct gdbarch *gdbarch, int num)
   return gdbarch_num_regs (gdbarch) + regnum;
 }
 
-
 /* Convert a dwarf, dwarf2, or ecoff register number to a GDB [1 *
    gdbarch_num_regs .. 2 * gdbarch_num_regs) REGNUM.  */
 
@@ -7954,7 +8408,6 @@ mips_register_sim_regno (struct gdbarch *gdbarch, int regnum)
     return LEGACY_SIM_REGNO_IGNORE;
 }
 
-
 /* Convert an integer into an address.  Extracting the value signed
    guarantees a correctly sign extended address.  */
 
@@ -8355,7 +8808,6 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), i,
 					    mips_gprs[i]);
 
-
       valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
 					  mips_regnum.lo, "lo");
       valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
@@ -8826,7 +9278,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   for (i = 0; i < ARRAY_SIZE (mips_numeric_register_aliases); i++)
     user_reg_add (gdbarch, mips_numeric_register_aliases[i].name,
-		  value_of_mips_user_reg, 
+		  value_of_mips_user_reg,
 		  &mips_numeric_register_aliases[i].regnum);
 
   return gdbarch;
@@ -8854,7 +9306,7 @@ show_mips_abi (struct ui_file *file,
   if (gdbarch_bfd_arch_info (current_inferior ()->arch  ())->arch
       != bfd_arch_mips)
     gdb_printf
-      (file, 
+      (file,
        "The MIPS ABI is unknown because the current architecture "
        "is not MIPS.\n");
   else
@@ -8865,7 +9317,7 @@ show_mips_abi (struct ui_file *file,
 
       if (global_abi == MIPS_ABI_UNKNOWN)
 	gdb_printf
-	  (file, 
+	  (file,
 	   "The MIPS ABI is set automatically (currently \"%s\").\n",
 	   actual_abi_str);
       else if (global_abi == actual_abi)
@@ -9117,3 +9569,4 @@ When non-zero, mips specific debugging is enabled."),
 				      currently %s.  */
 			     &setdebuglist, &showdebuglist);
 }
+
diff --git a/gdb/testsuite/gdb.arch/mips-64-r6.c b/gdb/testsuite/gdb.arch/mips-64-r6.c
new file mode 100644
index 00000000000..347f15e55b7
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/mips-64-r6.c
@@ -0,0 +1,915 @@
+/*
+   Copyright 2023-2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.
+*/
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+/* ============ macros from sim/testutils/mips/utils-r6.inc ============= */
+
+/* 58 is local label to exit with errcode != 0, indicating error.  */
+#define fp_assert(a, b) "beq " xstr (a) ", " xstr (b) ", 1f \n\t" \
+  "nop \n\t" \
+  "b 58f \n\t" \
+  "nop \n\t" \
+  "1: \n\t"
+
+/* Clobbers: $4,$6,$7.  */
+#define r6ck_1r(inst, a, ret) \
+  "li $4, " xstr (a) "    \n\t" \
+  "li $6, " xstr (ret) "  \n\t" \
+  xstr (inst) " $7, $4    \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$6,$7.  */
+#define r6ck_1dr(inst, a, ret) \
+  "ld $4, " xstr (a) "   \n\t" \
+  "ld $6, " xstr (ret) " \n\t" \
+  xstr (inst) " $7, $4   \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$5,$6,$7.  */
+#define r6ck_2r(inst, a, b, ret) \
+  "li $4, " xstr (a) "   \n\t" \
+  "li $5, " xstr (b) "   \n\t" \
+  "li $6, " xstr (ret) " \n\t" \
+  xstr (inst) " $7, $4, $5   \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$5,$6,$7.  */
+#define r6ck_2dr(inst, a, b, ret) \
+  "ld $4, " xstr (a) "    \n\t" \
+  "ld $5, " xstr (b) "    \n\t" \
+  "ld $6, " xstr (ret) "  \n\t" \
+  xstr (inst) " $7, $4, $5  \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$5,$6,$7.  */
+#define r6ck_2dr1i(inst, a, b, imm, ret) \
+  "ld $4, " xstr (a) "      \n\t" \
+  "ld $5, " xstr (b) "      \n\t" \
+  "ld $6, " xstr (ret) "    \n\t" \
+  xstr (inst) " $7, $4, $5, " xstr (imm) "  \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$6,$7.  */
+#define r6ck_1r1i(inst, a, imm, ret) \
+  "li $4, " xstr (a) "      \n\t" \
+  "li $6, " xstr (ret) "    \n\t" \
+  xstr (inst) " $7, $4, " xstr (imm) "  \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$6,$7.  */
+#define r6ck_1dr1i(inst, a, imm, ret) \
+  "ld $4, " xstr (a) "     \n\t" \
+  "ld $6, " xstr (ret) "   \n\t" \
+  xstr (inst) " $7, $4, " xstr (imm) "  \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$6.  */
+#define r6ck_0dr1i(inst, a, imm, ret) \
+  "ld $4, " xstr (a) "     \n\t" \
+  "ld $6, " xstr (ret) "   \n\t" \
+  xstr (inst) " $4, $4, " xstr (imm) "   \n\t" \
+  fp_assert ($6, $4)
+
+/* Clobbers: $4,$5,$6,$7.  */
+#define r6ck_2r1i(inst, a, b, imm, ret) \
+  "li $4, " xstr (a) "     \n\t" \
+  "li $5, " xstr (b) "     \n\t" \
+  "li $6, " xstr (ret) "   \n\t" \
+  xstr (inst) " $7, $4, $5, " xstr (imm) "   \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$5,$6,$7,$8,$f2,$f4,$f6.  */
+#define r6ck_3s(inst, a, b, c, ret) \
+  "li $4, " xstr (a) "     \n\t" \
+  "li $5, " xstr (b) "     \n\t" \
+  "li $6, " xstr (c) "     \n\t" \
+  "li $7, " xstr (ret) "   \n\t" \
+  "mtc1 $4, $f2           \n\t" \
+  "mtc1 $5, $f4           \n\t" \
+  "mtc1 $6, $f6           \n\t" \
+  xstr (inst) " $f2, $f4, $f6       \n\t" \
+  "mfc1 $8, $f2           \n\t" \
+  fp_assert ($7, $8)
+
+/* Clobbers: $4,$5,$6,$7,$f2,$f4.  */
+#define r6ck_2s(inst, a, b, ret) \
+  "li $4, " xstr (a) "       \n\t" \
+  "li $5, " xstr (b) "       \n\t" \
+  "li $6, " xstr (ret) "     \n\t" \
+  "mtc1 $4, $f2             \n\t" \
+  "mtc1 $5, $f4             \n\t" \
+  xstr (inst) " $f2, $f4     \n\t" \
+  "mfc1 $7, $f2             \n\t" \
+  fp_assert ($6, $7)
+
+/* Clobbers: $4,$5,$6,$7,$8,$9,$10,$f2,$f4.  */
+#define r6ck_2d(inst, a, b, ret) \
+  ".data                  \n\t" \
+  "1: .dword " xstr (a) "    \n\t" \
+  "2: .dword " xstr (b) "    \n\t" \
+  "3: .dword " xstr (ret) "  \n\t" \
+  ".text                   \n\t" \
+  "dla $4, 1b              \n\t" \
+  "dla $5, 2b              \n\t" \
+  "dla $6, 3b              \n\t" \
+  "ldc1 $f2, 0($4)        \n\t" \
+  "ldc1 $f4, 0($5)        \n\t" \
+  "lw $7, 0($6)           \n\t" \
+  "lw $8, 4($6)           \n\t" \
+  xstr (inst) " $f2, $f4   \n\t" \
+  "mfhc1 $9, $f2          \n\t" \
+  "mfc1 $10, $f2          \n\t" \
+  fp_assert ($7, $9) \
+  fp_assert ($8, $10)
+
+/* Clobbers: $2,$4,$5,$6,$7,$8,$9,$10,$f2,$f4,$f6.  */
+#define r6ck_3d(inst, a, b, c, ret) \
+  ".data                                \n\t" \
+  "1: .dword " xstr (a) "                  \n\t" \
+  "2: .dword " xstr (b) "                  \n\t" \
+  "3: .dword " xstr (c) "                  \n\t" \
+  "4: .dword " xstr (ret) "                \n\t" \
+  ".text                                \n\t" \
+  "dla $4, 1b                            \n\t" \
+  "dla $5, 2b                            \n\t" \
+  "dla $6, 3b                            \n\t" \
+  "dla $2, 4b                            \n\t" \
+  "ldc1 $f2, 0($4)                      \n\t" \
+  "ldc1 $f4, 0($5)                      \n\t" \
+  "ldc1 $f6, 0($6)                      \n\t" \
+  "lw $7, 0($2)                         \n\t" \
+  "lw $8, 4($2)                         \n\t" \
+  xstr (inst) " $f2, $f4, $f6            \n\t" \
+  "mfhc1 $9, $f2                        \n\t" \
+  "mfc1 $10, $f2                        \n\t" \
+  fp_assert ($7, $9) \
+  fp_assert ($8, $10)
+
+/* ============ macros from sim/testutils/mips/testutils.inc ============= */
+
+/* Put value 'val' into register 'reg'.
+   Clobbers: None.  */
+#define load32(reg, val) \
+  "li  " xstr (reg) ", " xstr (val) "     \n\t"
+
+/* Check whether two registers contain the same value.
+   Clobbers: None.  */
+#define checkreg(reg, expreg) \
+  ".set push         \n\t" \
+  ".set noat         \n\t" \
+  ".set noreorder    \n\t" \
+  "beq "  xstr (expreg) ", " xstr (reg) ", 901f    \n\t" \
+  "nop               \n\t" \
+  "b 58f              \n\t" \
+  "nop                \n\t" \
+  "901:                   \n\t" \
+  ".set pop           \n\t"
+
+/* Check if register 'reg' contains value 'val'.
+   Clobbers: $1.  */
+#define check32(reg, val) \
+  ".set push         \n\t" \
+  ".set noat         \n\t" \
+  load32 ($1, val) \
+  checkreg (reg, $1) \
+  ".set pop          \n\t"
+
+/* Checkpair based on endianess
+   Clobbers: $1.  */
+#define checkpair_xendian(lo, hi, base, ec, w) \
+  ".set noat               \n\t" \
+  "lw   $1, " xstr (ec) "   \n\t" \
+  "andi $1, $1, 0x1        \n\t" \
+  "beqz  $1, 2f            \n\t" \
+  ".set at                 \n\t" \
+  "1:                          \n\t" \
+  checkpair_be_##w (lo, hi, base) \
+  "b 3f                    \n\t" \
+  "nop                     \n\t" \
+  "2:                          \n\t" \
+  checkpair_le_##w (lo, hi, base) \
+  "3:                          \n\t"
+
+/* Check hi-lo register pair against data stored at base+o1 and base+o2.
+   Clobbers: $1 - $5.  */
+#define  checkpair(lo, hi, base, w, o1, o2) \
+  "move  $2, " xstr (lo) "   \n\t" \
+  "move  $3, " xstr (hi) "   \n\t" \
+  ".set noat                \n\t" \
+  "dla   $1, " xstr (base) "  \n\t" \
+  "l" xstr (w) " $4, " xstr (o1) "($1)  \n\t" \
+  "l" xstr (w)  " $5, " xstr (o2) "($1)  \n\t" \
+  ".set at                  \n\t" \
+  checkreg ($2, $4) \
+  checkreg ($3, $5)
+
+#define checkpair_le_d(lo, hi, base) \
+  checkpair (lo, hi, base, w, 0, 4)
+
+#define checkpair_be_d(lo, hi, base) \
+  checkpair (lo, hi, base, w, 4, 0)
+
+#define checkpair_le_q(lo, hi, base) \
+  checkpair (lo, hi, base, d, 0, 8)
+
+#define checkpair_be_q(lo, hi, base) \
+  checkpair (lo, hi, base, d, 8, 0)
+
+#define checkpair_qword(lo, hi, base, oe) \
+  checkpair_xendian (lo, hi, base, oe, q)
+
+#define checkpair_dword(lo, hi, base, oe) \
+  checkpair_xendian (lo, hi, base, oe, d)
+
+void
+abort (void);
+
+/* Tests branch instructions.  */
+
+int
+test_r6_branch (void)
+{
+/* Using volatile to prevent certain optimizations which could cause
+ * instruction deletion.
+ * 'err' identifies instruction which (eventually) caused error.
+ * (err == 0) ==> all instructions executed successfully.  */
+
+  volatile int err = -1;
+  volatile int a14 = 0xffffffff;
+  volatile int a13 = 0x123;
+  volatile int a12 = 0x45;
+  volatile int a7 = 0x45;
+  volatile int a8 = 0xfffffffe;
+  volatile int a9 = 2147483647;
+  volatile int a11 = 0;
+  volatile int a10 = 0;
+
+  asm (
+  ".set push             \n\t" /* Create new scope for asm configuration.  */
+  ".set noreorder        \n\t" /* Don't allow reordering of instructions.  */
+  "li %[err], 1                  \n\t"
+  "bovc %[a12], %[a13], Lfail    \n\t" /* BOVC */
+  "nop                           \n\t"
+  "bovc %[a9], %[a13], L2        \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L2:                           \n\t"
+  "li %[err], 2                  \n\t"
+  "bnvc %[a9], %[a13], Lfail     \n\t" /* BNVC */
+  "nop                           \n\t"
+  "bnvc %[a12], %[a13], L3       \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L3:                           \n\t"
+  "li %[err], 3                  \n\t"
+  "beqc %[a12], %[a13], Lfail    \n\t" /* BEQC */
+  "nop                           \n\t"
+  "beqc %[a12], %[a7], L4        \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L4:                           \n\t"
+  "li %[err], 4                  \n\t"
+  "bnec %[a12], %[a7], Lfail     \n\t" /* BNEC */
+  "nop                           \n\t"
+  "bnec %[a12], %[a13], L5       \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L5:                           \n\t"
+  "li %[err], 5                  \n\t"
+  "bltc %[a13], %[a12], Lfail    \n\t" /* BLTC */
+  "nop                           \n\t"
+  "bltc %[a12], %[a13], L6       \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L6:                           \n\t"
+  "L7:                           \n\t"
+  "li %[err], 7                  \n\t"
+  "bgec %[a12], %[a13], Lfail    \n\t" /* BGEC */
+  "nop                           \n\t"
+  "bgec %[a13], %[a12], L8       \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L8:                           \n\t"
+  "L9:                           \n\t"
+  "li %[err], 9                  \n\t"
+  "bltuc %[a14], %[a13], Lfail   \n\t" /* BLTUC */
+  "nop                           \n\t"
+  "bltuc %[a8], %[a14], L10      \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L10:                          \n\t"
+  "L11:                          \n\t"
+  "li %[err], 11                 \n\t"
+  "bgeuc %[a13], %[a14], Lfail   \n\t" /* BGEUC */
+  "nop                           \n\t"
+  "bgeuc %[a14], %[a8], L12      \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L12:                          \n\t"
+  "L13:                          \n\t"
+  "li %[err], 13                 \n\t"
+  "bltzc %[a13], Lfail           \n\t" /* BLTZC */
+  "nop                           \n\t"
+  "bltzc %[a11], Lfail           \n\t"
+  "nop                           \n\t"
+  "bltzc %[a14], L14             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L14:                          \n\t"
+  "li %[err], 14                 \n\t"
+  "blezc %[a13], Lfail           \n\t" /* BLEZC */
+  "nop                           \n\t"
+  "blezc %[a11], L145            \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L145:                         \n\t"
+  "blezc %[a14], L15             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L15:                          \n\t"
+  "li %[err], 15                 \n\t"
+  "bgezc %[a8], Lfail            \n\t" /* BGEZC */
+  "nop                           \n\t"
+  "bgezc %[a11], L155            \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L155:                         \n\t"
+  "bgezc %[a13], L16             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L16:                          \n\t"
+  "li %[err], 16                 \n\t"
+  "bgtzc %[a8], Lfail            \n\t" /* BGTZC */
+  "nop                           \n\t"
+  "bgtzc %[a11], Lfail           \n\t"
+  "nop                           \n\t"
+  "bgtzc %[a13], L17             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "li %[a10], 0                  \n\t"
+  "L17:                          \n\t"
+  "li %[err], 17                 \n\t"
+  "blezalc %[a12], Lfail         \n\t" /* BLEZALC */
+  "nop                           \n\t"
+  "blezalc %[a11], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L175            \n\t" /* BEQZC */
+  "nop                           \n\t"
+  "li %[err], 8531               \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L175:                         \n\t"
+  "li %[err], 23531              \n\t"
+  "blezalc %[a14], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L18             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L18:                          \n\t"
+  "li %[err], 18                 \n\t"
+  "bgezalc %[a14], Lfail         \n\t" /* BGEZALC */
+  "nop                           \n\t"
+  "bgezalc %[a11], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L185            \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L185:                         \n\t"
+  "bgezalc %[a12], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L19             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L19:                          \n\t"
+  "li %[err], 19                 \n\t"
+  "bgtzalc %[a14], Lfail         \n\t" /* BGTZALC */
+  "nop                           \n\t"
+  "bgtzalc %[a11], Lfail         \n\t"
+  "nop                           \n\t"
+  "bgtzalc %[a12], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L20             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L20:                          \n\t"
+  "li %[err], 20                 \n\t"
+  "bltzalc %[a12], Lfail         \n\t" /* BLTZALC */
+  "nop                           \n\t"
+  "bltzalc %[a11], Lfail         \n\t"
+  "nop                           \n\t"
+  "bltzalc %[a14], Lret          \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L21             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L21:                          \n\t"
+  "li %[err], 21                 \n\t"
+  "bc L22                        \n\t" /* BC */
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L22:                          \n\t"
+  "li %[err], 22                 \n\t"
+  "balc Lret                     \n\t" /* BALC */
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], L23             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L23:                          \n\t"
+  "li %[err], 23                 \n\t"
+  "jal GetPC                     \n\t" /* JAL */
+  "nop                           \n\t"
+  "jic $6, 4                     \n\t" /* JIC */
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L24:                          \n\t"
+  "li %[err], 24                 \n\t"
+  "li %[a10], 1                  \n\t"
+  "jal GetPC                     \n\t"
+  "nop                           \n\t"
+  "jialc $6, 20                  \n\t" /* JIALC */
+  "nop                           \n\t"
+  "beqzc %[a10], L25             \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "LJIALCRET:                    \n\t"
+  "li %[a10], 0                  \n\t"
+  "jr $31                        \n\t" /* JR */
+  "nop                           \n\t"
+  "L25:                          \n\t"
+  "li %[err], 25                 \n\t"
+  "jal GetPC                     \n\t"
+  "nop                           \n\t"
+  "move %[a11], $6               \n\t"
+  "nal                           \n\t"
+  "nop                           \n\t"
+  "addiu %[a11], 12              \n\t" /* ADDIU */
+  "beqc %[a11], $31, L26         \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "L26:                          \n\t"
+  "li %[err], 26                 \n\t"
+  "balc Lret                     \n\t"
+  "li %[a10], 1                  \n\t"
+  "beqzc %[a10], Lend            \n\t"
+  "nop                           \n\t"
+  "b Lfail                       \n\t"
+  "nop                           \n\t"
+  "Lret:                         \n\t"
+  "li %[a10], 0                  \n\t"
+  "daddiu $31, 4                 \n\t" /* DADDIU */
+  "jrc $31                       \n\t" /* JRC */
+  "nop                           \n\t"
+  "GetPC:                        \n\t"
+  "move $6, $31                  \n\t"
+  "jr $31                        \n\t"
+  "Lend:                         \n\t"
+  "li %[err], 0                  \n\t"
+  "Lfail:                        \n\t"
+  ".set pop                      \n\t" /* Restore previous config */
+  : [err] "+r"(err), [a14] "+r"(a14), [a13] "+r"(a13), [a12] "+r"(a12),
+    [a7] "+r"(a7), [a8] "+r"(a8), [a9] "+r"(a9), [a10] "+r"(a10), [a11] "+r"(a11)
+  : /* inputs */
+  : "$31", "$6" /* clobbers */
+  );
+
+  return err;
+}
+
+/* Tests forbidden slot branching i.e. conditional compact branch
+   instructions.  */
+
+int
+test_r6_forbidden (void)
+{
+  volatile int err = -1;
+  volatile int a4 = 0;
+  volatile int a2 = 0;
+  volatile int a1 = 0;
+
+  asm (
+  ".set push                         \n\t"
+  ".set noreorder                    \n\t"
+/* Test if FS is ignored when branch is taken */
+  "li %[err], 1                      \n\t"
+  "li %[a4], 0                       \n\t"
+  "beqzalc %[a4], L41                \n\t"
+  "li %[err], -85                    \n\t"
+  "L42:                              \n\t"
+  "b Lfail2                          \n\t"
+  "nop                               \n\t"
+  "L41:                              \n\t"
+  "blez %[err], Lfail2               \n\t"
+/* Test if FS is used when branch is not taken */
+  "li %[err], 2                      \n\t"
+  "li %[a4], 1                       \n\t"
+  "blezc %[a4], L43                  \n\t"
+  "addiu %[a4], %[a4], 1             \n\t"
+  "li %[a2], 2                       \n\t"
+  "beq %[a4], %[a2], L44             \n\t"
+  "nop                               \n\t"
+  "L43:                              \n\t"
+  "nop                               \n\t"
+  "b Lfail2                          \n\t"
+  "nop                               \n\t"
+  "L44:                              \n\t"
+  "li %[err], 3                      \n\t"
+  "li %[a4], 3                       \n\t"
+  "beqzalc %[a4], Lfail2             \n\t"
+/* Note: 'bc L45' instead nop would cause SegFault: Illegal instruction:
+   Forbidden slot causes an error when it contains a branch.  */
+  "nop                               \n\t"
+  "b Lend2                           \n\t"
+  "nop                               \n\t"
+  "L45:                              \n\t"
+  "nop                               \n\t"
+  "b Lfail2                          \n\t"
+  "nop                               \n\t"
+  "Lend2:                            \n\t"
+  "li %[err], 0                      \n\t"
+  "Lfail2:                           \n\t"
+  "nop                               \n\t"
+  ".set pop                          \n\t"
+  : [err] "+r" (err), [a4] "+r"(a4), [a2] "+r"(a2), [a1] "+r"(a1) /* outputs */
+  : /* inputs */
+  : "$31" /* clobbers */
+  );
+
+  return err;
+}
+
+int
+test_r6_64 (void)
+{
+  volatile int err = 0;
+
+  asm (
+  ".set push                                   \n\t"
+  ".set noreorder                              \n\t"
+  ".data                                       \n\t"
+  "dval: .dword 0xaa55bb66cc77dd88             \n\t"
+  "d1:   .dword 0xaaaabbbbccccdddd             \n\t"
+  "d5:   .dword 0x00000000000000dd             \n\t"
+  "d7:   .dword 0xeeeeffff00001111             \n\t"
+  "d8:   .dword 0xbbccccddddeeeeff             \n\t"
+  "d9:   .dword 0x000000ddaaaabbbb             \n\t"
+  "dval1: .word 0x1234abcd                     \n\t"
+  "dval2: .word 0xffee0000                     \n\t"
+  "dval3: .dword 0xffffffffffffffff            \n\t"
+  "  .fill 240,1,0                             \n\t"
+  "dval4: .dword 0x5555555555555555            \n\t"
+  "  .fill  264,1,0                            \n\t"
+
+/* Register $11 stores instruction currently being tested and hence
+ * identifies error if it occurs.  */
+  ".text                                       \n\t"
+
+  "li $11, 1                                   \n\t" /* Test DALIGN */
+  r6ck_2dr1i (dalign, d7, d1, 3, d8)
+  r6ck_2dr1i (dalign, d1, d5, 4, d9)
+
+  "li $11, 2                                   \n\t" /* Test LDPC */
+  "ld $5, dval                                 \n\t"
+  "nop                                         \n\t"
+  "ldpc $4, dval                               \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 3                                   \n\t" /* Test LWUPC */
+  "lwu $5, dval1                               \n\t"
+  "lwupc $4, dval1                             \n\t"
+  fp_assert ($4, $5)
+  "lwu $5, dval2                               \n\t"
+  "lwupc $4, dval2                             \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 4                                   \n\t" /* Test LLD */
+  "ld $5, dval3                                \n\t"
+  "dla $3, dval4                               \n\t"
+  "lld $4, -248($3)                            \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 5                                   \n\t" /* Test SCD */
+  "lld $4, -248($3)                            \n\t"
+  "dli $4, 0xafaf                              \n\t"
+  "scd $4, -248($3)                            \n\t"
+  "ld $5, dval3                                \n\t"
+  "dli $4, 0xafaf                              \n\t"
+  fp_assert ($4, $5)
+
+  "Lend3:                                      \n\t"
+  "li $11, 0                                   \n\t"
+  "58:                                         \n\t"
+  "move %[err], $11                            \n\t"
+  ".set pop                                    \n\t"
+   : [err] "+r" (err) /* outputs */
+   :                  /* inputs */
+   : "$3", "$4", "$5", "$6", "$7", "$11" /* clobbers */
+  );
+
+  return err;
+}
+
+int
+test_r6 (void)
+{
+  volatile int err = 0;
+
+  asm (
+  ".set push                                                   \n\t"
+  ".set noreorder                                              \n\t"
+  ".data                                                       \n\t"
+  "dval_1:  .word 0xabcd1234                                   \n\t"
+  "dval_2: .word 0x1234eeff                                    \n\t"
+  ".fill 248,1,0                                               \n\t"
+  "dval_3: .word 0x55555555                                    \n\t"
+  ".fill  260,1,0                                              \n\t"
+  "dval_4: .word 0xaaaaaaaa                                    \n\t"
+  ".text                                                       \n\t"
+  "li $11, 1            \n\t" /* ADDIUPC */
+  "jal GetPC_2                                               \n\t"
+  "nop                                                       \n\t"
+  "addiu $4, $6, 8                                           \n\t"
+  "addiupc $5, 4                                             \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 2            \n\t" /* AUIPC */
+  "jal GetPC_2                                               \n\t"
+  "nop                                                       \n\t"
+  "addiu $4, $6, 8                                           \n\t"
+  "aui $4, $4, 8                                             \n\t"
+  "auipc $5, 8                                               \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 3            \n\t" /* ALUIPC */
+  "jal GetPC_2                                               \n\t"
+  "nop                                                       \n\t"
+  "addiu $4, $6, 16                                          \n\t"
+  "aui $4, $4, 8                                             \n\t"
+  "li $7, 0xffff0000                                         \n\t"
+  "and $4, $4, $7                                            \n\t"
+  "aluipc $5, 8                                              \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 4            \n\t" /* LWPC */
+  "lw $5, dval_1                                             \n\t"
+  "lwpc $4, dval_1                                           \n\t"
+  fp_assert ($4, $5)
+  "lw $5, dval_2                                             \n\t"
+  "lwpc $4, dval_2                                           \n\t"
+  fp_assert ($4, $5)
+
+/* Testing LL follows.
+ * NOTE: May be redundant because SC already contains LL in its test.  */
+  "li $11, 5                                                \n\t"
+  "lw $5, dval_2                                             \n\t"
+  "dla $3, dval_3                                            \n\t"
+  "ll $4, -252($3)                                           \n\t"
+  fp_assert ($4, $5)
+
+  "li $11, 6        \n\t"    /* LL-SC sequence */
+  "ll $4, -252($3)                                           \n\t"
+  "li $4, 0xafaf                                             \n\t"
+  "sc $4, -252($3)                                           \n\t"
+  "lw $5, dval_2                                             \n\t"
+  "li $4, 0xafaf                                             \n\t"
+  fp_assert ($4, $5)
+  "b Lend4                                                   \n\t"
+  "nop                                                       \n\t"
+  "GetPC_2:                                                  \n\t"
+  "move $6, $31                                              \n\t"
+  "jr $31                                                    \n\t"
+  "Lend4:                                                    \n\t"
+  "li $11, 0                                                 \n\t"
+  "58:                                                       \n\t"
+  "move %[err], $11                                          \n\t"
+  ".set pop                                                  \n\t"
+  : [err] "+r"(err)  /* outputs */
+  : /* inputs */
+  : "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10",
+    "$11", "$31" /* clobbers */
+  );
+
+  return err;
+}
+
+/* For fpu-branching testing, bc1eq/eqz are enough.  */
+
+int
+test_r6_fpu (void)
+{
+  volatile int err = 0;
+
+  asm (
+  ".set push                               \n\t"
+  ".set noreorder                          \n\t"
+/* Test qNaN format is 754-2008 */
+  "li $11, 1                               \n\t"
+  "li $4, 0x0                              \n\t"
+  "li $5, 0x0                              \n\t"
+  "li $6, 0x7fc00000                       \n\t"
+  "mtc1 $4, $f2                            \n\t"
+  "mtc1 $5, $f4                            \n\t"
+  "div.s $f6, $f2, $f4                     \n\t"
+  "mfc1 $8, $f6                            \n\t"
+  fp_assert ($6, $8)
+
+  "li $11, 2                                \n\t"  /* bc1eqz */
+  "li $10, 0x01                             \n\t"
+  "mtc1 $10, $f2                            \n\t"
+  "mtc1 $0, $f4                             \n\t"
+  "bc1eqz $f2, 58f                          \n\t"
+  "nop                                      \n\t"
+  "bc1eqz $f4, L62                          \n\t"
+  "nop                                      \n\t"
+  "b 58f                                    \n\t"
+  "nop                                      \n\t"
+"L62:                                       \n\t"
+  "li $11, 3                                \n\t"  /* bc1nez */
+  "bc1nez $f4, 58f                          \n\t"
+  "nop                                      \n\t"
+  "bc1nez $f2, Lend8                        \n\t"
+  "nop                                      \n\t"
+  "b 58f                                    \n\t"
+  "nop                                      \n\t"
+"Lend8:                                     \n\t"
+  "li $11, 0                                \n\t"
+  "58:                                      \n\t"
+  "move %[err], $11                         \n\t"
+  ".set pop                                 \n\t"
+  : [err] "+r"(err)  /* outputs */
+  :                  /* inputs */
+  : "$4", "$5", "$6", "$8", "$10", "$11", "$31",
+    "$f2", "$f4", "$f6" /* clobbers */
+  );
+
+  return err;
+}
+
+/* R6 specific tests for mips64 (64-bit).  */
+
+int
+test_r6_llsc_dp (void)
+{
+  volatile int err = 0;
+
+  asm (
+  ".set push                                            \n\t"
+  ".set noreorder                                       \n\t"
+  ".data                                                \n\t"
+  ".align 16                                            \n\t"
+  "test_data:                                           \n\t"
+  ".word 0xaaaaaaaa                                     \n\t"
+  ".word 0xbbbbbbbb                                     \n\t"
+  ".word 0xcccccccc                                     \n\t"
+  ".word 0xdddddddd                                     \n\t"
+  ".align 16                                            \n\t"
+  "end_check:                                           \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0x1                                            \n\t"
+  ".text                                                \n\t"
+  "li $11, 1                                            \n\t" /* LLWP */
+  "llwp $2, $3, test_data                               \n\t"
+  checkpair_dword ($2, $3, test_data, end_check)
+  "sll $2, $2, 1                                        \n\t"
+  "srl $3, $3, 1                                        \n\t"
+  "move  $s0, $2                                        \n\t"
+  "scwp $2, $3, test_data                               \n\t"
+  check32 ($2, 1)
+  checkpair_dword ($s0, $3, test_data, end_check)
+  /* Test SCWP, done */
+  "li $11, 2                                            \n\t"
+  "li $11, 3                                            \n\t" /* LLDP */
+  "lldp $2, $3, test_data                               \n\t"
+  checkpair_qword ($2, $3, test_data, end_check)
+  "dsll $2, $2, 1                                       \n\t"
+  "dsrl $3, $3, 1                                       \n\t"
+  "move $s0, $2                                         \n\t"
+  "scdp $2, $3, test_data                               \n\t"
+  check32 ($2, 1)
+  checkpair_qword ($s0, $3, test_data, end_check)
+  "li $11, 4                                            \n\t" /* SCDP done */
+  "Lend5:                                               \n\t"
+  "li $11, 0                                            \n\t"
+  "58:                                                  \n\t"
+  "move %[err], $11                                     \n\t"
+  ".set pop                                             \n\t"
+  : [err] "+r"(err) /* outputs */
+  : /* inputs */
+  : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11",
+    "$31", "$s0"  /* clobbers */
+  );
+
+  return err;
+}
+
+/* R6 specific tests for mips32 (32-bit).  */
+
+int
+test_r6_llsc_wp (void)
+{
+  volatile int err = 0;
+
+  asm (
+  ".set push                                            \n\t"
+  ".set noreorder                                       \n\t"
+  ".data                                                \n\t"
+  ".align 8                                             \n\t"
+  "test_data_2:                                         \n\t"
+  ".word 0xaaaaaaaa                                     \n\t"
+  ".word 0xbbbbbbbb                                     \n\t"
+  ".align 8                                             \n\t"
+  "end_check_2:                                         \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0                                              \n\t"
+  ".byte 0x1                                            \n\t"
+  ".text                                                \n\t"
+  "li $11, 1                                            \n\t" /* Test LLWP */
+  "llwp $2, $3, test_data_2                             \n\t"
+  checkpair_dword ($2, $3, test_data_2, end_check_2)
+  "sll $2, $2, 1                                        \n\t"
+  "srl $3, $3, 1                                        \n\t"
+  "move  $s0, $2                                        \n\t"
+  "scwp $2, $3, test_data_2                             \n\t"
+  check32 ($2, 1)
+  checkpair_dword ($s0, $3, test_data_2, end_check_2)
+/* Test SCWP, done.  */
+  "li $11, 2                                            \n\t"
+  "Lend6:                                               \n\t"
+  "li $11, 0                                            \n\t"
+  "58:                                                  \n\t"
+  "move %[err], $11                                     \n\t"
+  ".set pop                                             \n\t"
+  : [err] "+r"(err) /* outputs */
+  : /* inputs */
+  : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10",
+    "$11", "$31", "$s0"  /* clobbers */
+  );
+
+  return err;
+}
+
+/* Any test_r6_* function returns non-zero => failure.  */
+#define EXPECT(X) if ((X)) abort ();
+
+int
+main (void)
+{
+  EXPECT (test_r6_branch ());
+
+  EXPECT (test_r6_forbidden ());
+
+  EXPECT (test_r6_64 ());
+
+  EXPECT (test_r6 ());
+
+  EXPECT (test_r6_fpu ());
+
+  EXPECT (test_r6_llsc_dp ());
+
+  EXPECT (test_r6_llsc_wp ());
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/mips-64-r6.exp b/gdb/testsuite/gdb.arch/mips-64-r6.exp
new file mode 100644
index 00000000000..5f74b472edf
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/mips-64-r6.exp
@@ -0,0 +1,76 @@
+# Copyright (C) 2023-2024 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/>.
+
+################################################################
+################## MIPS Release 6 patch tests ##################
+################################################################
+
+# Send 'si' to gdb until inferior exits.
+proc stepi {} {
+    global gdb_prompt
+    set timeout [get_largest_timeout]
+    set start [timestamp]
+    while { [timestamp] - $start < 3*$timeout } {
+       gdb_test_multiple "stepi" ""  {
+           -re ".*exited normally.*" {
+               pass "success"
+               return
+           }
+           -re ".*The program is not being run.*" {
+               fail "failure"
+               return
+           }
+           -re ".*Breakpoint.*test_.*$gdb_prompt $" {
+           }
+           -re "$gdb_prompt.*" {
+           }
+       }
+    }
+    fail "test failed"
+    return
+}
+
+require {istarget "*mips*"}
+
+set testfile "mips-64-r6"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if {[prepare_for_testing "failed to prepare" "${binfile}" "${srcfile}" {debug nowarnings}]} {
+    untested "failed to prepare"
+    return
+}
+
+# Native needs run.
+if { ![runto_main] } {
+    untested "couldn't run to main"
+    return
+}
+
+set tests ""
+foreach n [list "r6_branch" "r6_forbidden" "r6_64" "r6" "r6_fpu" "r6_llsc_dp" "r6_llsc_wp"] {
+    lappend tests "test_$n"
+}
+
+# Put breakpoint on each test-function
+foreach func $tests {
+    if {![gdb_breakpoint "$func"]} {
+       untested "couldn't put breakpoint to $func"
+       return
+    }
+}
+
+# Step through the binary
+stepi
-- 
2.34.1




Kevin, thank you so much for your suggestions.


Kind Regards, 
Milica

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH^8] gdb: mips: Add MIPSR6 support
  2024-12-13 20:29 [PATCH^8] gdb: mips: Add MIPSR6 support Milica Matic
@ 2025-01-07 20:49 ` Kevin Buettner
  2025-01-08  2:25   ` Maciej W. Rozycki
  0 siblings, 1 reply; 3+ messages in thread
From: Kevin Buettner @ 2025-01-07 20:49 UTC (permalink / raw)
  To: Milica Matic; +Cc: gdb-patches

Hi Milica,

While applying your patch locally, I saw:

warning: squelched 8 whitespace errors
warning: 13 lines add whitespace errors.

I suggest doing a "git am" using the patch that you sent to the list
to identify them.

Additional comments inline, below...

On Fri, 13 Dec 2024 20:29:05 +0000
Milica Matic <milica.matic@htecgroup.com> wrote:

> +/* Calculate address of next instruction after BLEZ.  */
> +
> +static CORE_ADDR
> +mips32_blez_pc (struct gdbarch *gdbarch, struct regcache *regcache,
> +	       ULONGEST inst, CORE_ADDR pc, int invert)
> +{
> +  int rs = itype_rs (inst);
> +  int rt = itype_rt (inst);
> +  LONGEST val_rs = regcache_raw_get_signed (regcache, rs);
> +  LONGEST val_rt = regcache_raw_get_signed (regcache, rt);
> +  ULONGEST uval_rs = regcache_raw_get_unsigned (regcache, rs);
> +  ULONGEST uval_rt = regcache_raw_get_unsigned (regcache, rt);
> +  int taken = 0;
> +  int delay_slot_size = 4;
> +
> +  /* BLEZ, BLEZL, BGTZ, BGTZL  */
> +  if (rt == 0)
> +    taken = (val_rs <= 0);
> +  else if (is_mipsr6_isa (gdbarch))
> +    {
> +      /* BLEZALC, BGTZALC  */
> +      if (rs == 0 && rt != 0)
> +       taken = (val_rt <= 0);
> +      /* BGEZALC, BLTZALC  */
> +      else if (rs == rt && rt != 0)
> +       taken = (val_rt >= 0);
> +      /* BGEUC, BLTUC  */
> +      else if (rs != rt && rs != 0 && rt != 0)
> +       taken = (uval_rs >= uval_rt);
> +
> +      /* Step through the forbidden slot to avoid repeated exceptions we do
> +	not currently have access to the BD bit when hitting a breakpoint
> +	and therefore cannot tell if the breakpoint hit on the branch or the
> +	forbidden slot.  */
> +      /* delay_slot_size = 0;  */

Should "delay_slot_size = 0;" be commented out?  If so, I suggest
removing the entire line.  And, if it's removed, the comment preceding
it probably doesn't make sense either.

> +    }
> +
> +  if (invert)
> +    taken = !taken;
> +
> +  /* Calculate branch target.  */
> +  if (taken)
> +    pc += mips32_relative_offset (inst);
> +  else
> +    pc += delay_slot_size;
> +
> +  return pc;
> +}
>  
>  /* Determine where to set a single step breakpoint while considering
>     branch prediction.  */
> @@ -1642,58 +1751,66 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
>    struct gdbarch *gdbarch = regcache->arch ();
>    unsigned long inst;
>    int op;
> +  int mips64bitreg = 0;
> +
> +  if (mips_isa_regsize (gdbarch) == 8)
> +    mips64bitreg = 1;
> +
>    inst = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL);
>    op = itype_op (inst);
> -  if ((inst & 0xe0000000) != 0)		/* Not a special, jump or branch
> -					   instruction.  */
> +  if ((inst & 0xe0000000) != 0) /* Not a special, jump or branch instruction. */
> +    {
> +      if (op >> 2 == 5 && ((op & 0x02) == 0 || itype_rt (inst) == 0))
> +    /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
>      {
> -      if (op >> 2 == 5)
> -	/* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
> +      switch (op & 0x03)
>  	{
> -	  switch (op & 0x03)
> -	    {
> -	    case 0:		/* BEQL */
> -	      goto equal_branch;
> -	    case 1:		/* BNEL */
> -	      goto neq_branch;
> -	    case 2:		/* BLEZL */
> -	      goto less_branch;
> -	    case 3:		/* BGTZL */
> -	      goto greater_branch;
> -	    default:
> -	      pc += 4;
> -	    }
> +	case 0:		/* BEQL */
> +	  goto equal_branch;
> +	case 1:		/* BNEL */
> +	  goto neq_branch;
> +	case 2:		/* BLEZL */
> +	  goto lez_branch;
> +	case 3:		/* BGTZL */
> +	  goto greater_branch;
> +	default:
> +	  pc += 4;
>  	}
> +    }

The indentation here somehow got messed up here.  After applying your
patch, I see:

  if ((inst & 0xe0000000) != 0) /* Not a special, jump or branch instruction. */
    {
      if (op >> 2 == 5 && ((op & 0x02) == 0 || itype_rt (inst) == 0))
    /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
    {
      switch (op & 0x03)
	{
	case 0:		/* BEQL */
	  goto equal_branch;
	case 1:		/* BNEL */
	  goto neq_branch;
	case 2:		/* BLEZL */
	  goto lez_branch;
	case 3:		/* BGTZL */
	  goto greater_branch;
	default:
	  pc += 4;
	}
    }

Note that the second '{' and the block that goes along with it are not
correctly indented for the 'if' statement just above it.  I believe
that there are further indentation problems later on too; I'll point
out a few of them, but I'll leave it to you to find and fix the rest.

[...]
> @@ -1763,22 +2000,38 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
>  		  pc += 8;	/* after the delay slot */
>  		break;
>  	      case 0x1c:	/* BPOSGE32 */
> +	     case 0x1d:        /* BPOSGE32C  */

Indentation is off by one space here.  This is what I see after applying your patch:

	      case 0x1c:	/* BPOSGE32 */
	     case 0x1d:        /* BPOSGE32C  */
	      case 0x1e:	/* BPOSGE64 */

>  	      case 0x1e:	/* BPOSGE64 */
>  		pc += 4;
>  		if (itype_rs (inst) == 0)
>  		  {
>  		    unsigned int pos = (op & 2) ? 64 : 32;
>  		    int dspctl = mips_regnum (gdbarch)->dspctl;
> +		   int delay_slot_size = 4;

Likewise.  (Off by one space.)

>  
>  		    if (dspctl == -1)
>  		      /* No way to handle; it'll most likely trap anyway.  */
>  		      break;
>  
> +		   /* BPOSGE32C  */
> +		   if (op == 0x1d)
> +		     {
> +		       if (!is_mipsr6_isa (gdbarch))
> +			 break;
> +
> +		       /* Step through the forbidden slot to avoid repeated
> +			  exceptions we do not currently have access to the BD
> +			  bit when hitting a breakpoint and therefore cannot
> +			  tell if the breakpoint hit on the branch or the
> +			  forbidden slot.  */
> +		       /* delay_slot_size = 0;  */
> +		     }

Likewise, for the above block.

> +
>  		    if ((regcache_raw_get_unsigned (regcache,
>  						    dspctl) & 0x7f) >= pos)
>  		      pc += mips32_relative_offset (inst);
>  		    else
> -		      pc += 4;
> +		     pc += delay_slot_size;

Likewise.

>  		  }
>  		break;
>  		/* All of the other instructions in the REGIMM category */
> @@ -1812,19 +2065,14 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
>  	  else
>  	    pc += 8;
>  	  break;
> -	case 6:		/* BLEZ, BLEZL */
> -	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) <= 0)
> -	    pc += mips32_relative_offset (inst) + 4;
> -	  else
> -	    pc += 8;
> +       case 6:         /* BLEZ, BLEZL, BLEZALC, BGEZALC, BGEUC  */
> +       lez_branch:
> +	 pc = mips32_blez_pc (gdbarch, regcache, inst, pc + 4, 0);

Likewise.

>  	  break;
>  	case 7:
>  	default:
> -	greater_branch:	/* BGTZ, BGTZL */
> -	  if (regcache_raw_get_signed (regcache, itype_rs (inst)) > 0)
> -	    pc += mips32_relative_offset (inst) + 4;
> -	  else
> -	    pc += 8;
> +       greater_branch: /* BGTZ, BGTZL, BGTZALC, BLTZALC, BLTUC  */
> +	 pc = mips32_blez_pc (gdbarch, regcache, inst, pc + 4, 1);

Likewise.

[...]

> +/* Return non-zero if the MIPS instruction INSN is a compact branch
> +   or jump.  A value of 1 indicates an unconditional compact branch
> +   and a value of 2 indicates a conditional compact branch.  */
> +
> +static int
> +mips32_instruction_is_compact_branch (struct gdbarch *gdbarch, ULONGEST insn)
> +{
> +  switch (itype_op (insn))
> +    {
> +    /* BC  */
> +    case 50:
> +    /* BALC  */
> +    case 58:
> +      if (is_mipsr6_isa (gdbarch))
> +       return 1;
> +      break;
> +    /* BOVC, BEQZALC, BEQC  */
> +    case 8:
> +    /* BNVC, BNEZALC, BNEC  */
> +    case 24:
> +      if (is_mipsr6_isa (gdbarch))

Up to this point, indentation appears correct for the new function
'mips32_instruction_is_compact_branch', but...

> +       return 2;

It's wrong for the line above.  That line is indented by 7 spaces, but it should be 8, which'll
turn it into a tab.

> +      break;
> +    /* BEQZC, JIC  */
> +    case 54:
> +    /* BNEZC, JIALC  */
> +    case 62:
> +      if (is_mipsr6_isa (gdbarch))
> +       /* JIC, JIALC are unconditional  */
> +       return (itype_rs (insn) == 0) ? 1 : 2;

Likewise for the two lines above.  (7 spaces are used instead of 8 which converts to a tab.)

> +      break;
> +    /* BLEZC, BGEZC, BGEC  */
> +    case 22:
> +    /* BGTZC, BLTZC, BLTC  */
> +    case 23:
> +    /* BLEZALC, BGEZALC, BGEUC  */
> +    case 6:
> +    /* BGTZALC, BLTZALC, BLTUC  */
> +    case 7:
> +      if (is_mipsr6_isa (gdbarch)
> +	 && itype_rt (insn) != 0)
> +       return 2;

Likewise for the "return 2;" line.

> +      break;
> +    /* BPOSGE32C  */
> +    case 1:
> +      if (is_mipsr6_isa (gdbarch)
> +	 && itype_rt (insn) == 0x1d && itype_rs (insn) == 0)
> +       return 2;

And here too.

[...]
>  static CORE_ADDR
> @@ -3490,7 +3800,8 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
>        reg = high_word & 0x1f;
>  
>        if (high_word == 0x27bd		/* addiu $sp,$sp,-i */
> -	  || high_word == 0x23bd	/* addi $sp,$sp,-i */
> +	 || (high_word == 0x23bd       /* addi $sp,$sp,-i  */
> +	     && !is_mipsr6_isa (gdbarch))
>  	  || high_word == 0x67bd)	/* daddiu $sp,$sp,-i */

Indentation problem here too.  The '||' line that you added should have the same indentation
level as the '|| high_word == 0x67bd)	/* daddiu $sp,$sp,-i */' line.

Okay, so I'm going to stop here.  Scanning ahead, I saw other indentation problems which
are similar to those mentioned above.  I'll leave it to you to find and fix them...

I did notice that you fixed some of the existing whitespace problems in this file, including
removal of extraneous blank lines and spaces at the end of a line.  Thanks for doing that!

Kevin


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH^8] gdb: mips: Add MIPSR6 support
  2025-01-07 20:49 ` Kevin Buettner
@ 2025-01-08  2:25   ` Maciej W. Rozycki
  0 siblings, 0 replies; 3+ messages in thread
From: Maciej W. Rozycki @ 2025-01-08  2:25 UTC (permalink / raw)
  To: Kevin Buettner; +Cc: Milica Matic, gdb-patches

On Tue, 7 Jan 2025, Kevin Buettner wrote:

> > +/* Calculate address of next instruction after BLEZ.  */
> > +
> > +static CORE_ADDR
> > +mips32_blez_pc (struct gdbarch *gdbarch, struct regcache *regcache,
> > +	       ULONGEST inst, CORE_ADDR pc, int invert)
> > +{
> > +  int rs = itype_rs (inst);
> > +  int rt = itype_rt (inst);
> > +  LONGEST val_rs = regcache_raw_get_signed (regcache, rs);
> > +  LONGEST val_rt = regcache_raw_get_signed (regcache, rt);
> > +  ULONGEST uval_rs = regcache_raw_get_unsigned (regcache, rs);
> > +  ULONGEST uval_rt = regcache_raw_get_unsigned (regcache, rt);
> > +  int taken = 0;
> > +  int delay_slot_size = 4;
> > +
> > +  /* BLEZ, BLEZL, BGTZ, BGTZL  */
> > +  if (rt == 0)
> > +    taken = (val_rs <= 0);
> > +  else if (is_mipsr6_isa (gdbarch))
> > +    {
> > +      /* BLEZALC, BGTZALC  */
> > +      if (rs == 0 && rt != 0)
> > +       taken = (val_rt <= 0);
> > +      /* BGEZALC, BLTZALC  */
> > +      else if (rs == rt && rt != 0)
> > +       taken = (val_rt >= 0);
> > +      /* BGEUC, BLTUC  */
> > +      else if (rs != rt && rs != 0 && rt != 0)
> > +       taken = (uval_rs >= uval_rt);
> > +
> > +      /* Step through the forbidden slot to avoid repeated exceptions we do
> > +	not currently have access to the BD bit when hitting a breakpoint
> > +	and therefore cannot tell if the breakpoint hit on the branch or the
> > +	forbidden slot.  */
> > +      /* delay_slot_size = 0;  */
> 
> Should "delay_slot_size = 0;" be commented out?  If so, I suggest
> removing the entire line.  And, if it's removed, the comment preceding
> it probably doesn't make sense either.

 I think the comment refers to the current situation rather than where 
`delay_slot_size' would be set to 0.

 The comment also seems at best misleading to me.  With hosted targets the 
BD bit is accessible via $cause register, which I do believe all the MIPS 
OSes supply unmodified.  With EJTAG targets or bare metal simulators the 
target description should supply $debug register providing the analogous 
DBD bit.  The backend could use that information to determine whether a 
breakpoint has hit in a delay slot; this has been considered before.

 However we have chosen to refrain from placing breakpoints in delay slots 
regardless, as that would complicate handling.  So it seems to me that the 
note should instead be rephrased accordingly and moved to the function's 
introductory comment...

> > +    }
> > +
> > +  if (invert)
> > +    taken = !taken;
> > +
> > +  /* Calculate branch target.  */

 ... and then "or skip over the forbidden" slot added here...

> > +  if (taken)
> > +    pc += mips32_relative_offset (inst);
> > +  else
> > +    pc += delay_slot_size;

 ... and then the otherwise unused variable removed (i.e. "pc += 4" here).

 Similarly in the other place, taking into account the slightly different 
context (so I think the comments ought to refer to delay slots in general 
rather than just the forbidden slots, which are a particular subset).

  Maciej

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-01-08  2:26 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-13 20:29 [PATCH^8] gdb: mips: Add MIPSR6 support Milica Matic
2025-01-07 20:49 ` Kevin Buettner
2025-01-08  2:25   ` Maciej W. Rozycki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox