* [PATCH 0/2] gdb: mips: Add MIPSR6 support
@ 2025-02-19 13:01 Milica Matic
2025-02-19 13:01 ` [PATCH v12 " Milica Matic
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Milica Matic @ 2025-02-19 13:01 UTC (permalink / raw)
To: gdb-patches
Cc: Milica Matic, Djordje Todorovic, Milos Kalicanin, simark, cfu,
aburgess, kevinb, macro
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 v11: Splitting the patch into two parts: Formatting the
mips-tdep.c file and "gdb: mips: Add MIPSR6 support".
[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
2025-02-19
Andrew Bennett <andrew.bennett@imgtec.com>
Matthew Fortune <matthew.fortune@mips.com>
Faraz Shahbazker <fshahbazker@wavecomp.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v12 0/2] gdb: mips: Add MIPSR6 support
2025-02-19 13:01 [PATCH 0/2] gdb: mips: Add MIPSR6 support Milica Matic
@ 2025-02-19 13:01 ` Milica Matic
2025-02-19 13:01 ` [PATCH v12 1/2] Apply coding guidelines Milica Matic
2025-02-19 13:01 ` [PATCH v12 2/2] gdb: mips: Add MIPSR6 support Milica Matic
2 siblings, 0 replies; 9+ messages in thread
From: Milica Matic @ 2025-02-19 13:01 UTC (permalink / raw)
To: gdb-patches
Cc: Milica Matic, Djordje Todorovic, Milos Kalicanin, simark, cfu,
aburgess, kevinb, macro
Milica Matic (2):
Apply coding guidelines
gdb: mips: Add MIPSR6 support
gdb/mips-tdep.c | 941 ++++++++++++++++++++------
gdb/testsuite/gdb.arch/mips-64-r6.c | 916 +++++++++++++++++++++++++
gdb/testsuite/gdb.arch/mips-64-r6.exp | 76 +++
3 files changed, 1718 insertions(+), 215 deletions(-)
create mode 100644 gdb/testsuite/gdb.arch/mips-64-r6.c
create mode 100644 gdb/testsuite/gdb.arch/mips-64-r6.exp
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v12 1/2] Apply coding guidelines
2025-02-19 13:01 [PATCH 0/2] gdb: mips: Add MIPSR6 support Milica Matic
2025-02-19 13:01 ` [PATCH v12 " Milica Matic
@ 2025-02-19 13:01 ` Milica Matic
2025-02-19 23:21 ` Kevin Buettner
2025-02-19 13:01 ` [PATCH v12 2/2] gdb: mips: Add MIPSR6 support Milica Matic
2 siblings, 1 reply; 9+ messages in thread
From: Milica Matic @ 2025-02-19 13:01 UTC (permalink / raw)
To: gdb-patches
Cc: Milica Matic, Djordje Todorovic, Milos Kalicanin, simark, cfu,
aburgess, kevinb, macro
Format mips-tdep.c code as described on links:
https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards
https://www.gnu.org/prep/standards/standards.html#Comments
---
gdb/mips-tdep.c | 395 +++++++++++++++++++++++++++---------------------
1 file changed, 221 insertions(+), 174 deletions(-)
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index a28c99d3366..dbf0b1aa5df 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -1,6 +1,6 @@
/* Target-dependent code for the MIPS architecture, for GDB, the GNU Debugger.
- Copyright (C) 1988-2024 Free Software Foundation, Inc.
+ Copyright (C) 1988-2025 Free Software Foundation, Inc.
Contributed by Alessandro Forin(af@cs.cmu.edu) at CMU
and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
@@ -76,8 +76,8 @@ 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 *);
-/* 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. */
+/* 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)
/* The sizes of floating point registers. */
@@ -220,6 +220,7 @@ static std::string mips_disassembler_options;
to the ABI we have selected, perhaps via a `set mips abi ...'
override, rather than ones inferred from the ABI set in the ELF
headers of the binary file selected for debugging. */
+
static const char mips_disassembler_options_o32[] = "gpr-names=32";
static const char mips_disassembler_options_n32[] = "gpr-names=n32";
static const char mips_disassembler_options_n64[] = "gpr-names=64";
@@ -633,7 +634,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 +657,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.
@@ -668,6 +668,7 @@ mips_register_name (struct gdbarch *gdbarch, int regno)
configured to be 32-bits wide. The registers that the user
sees - the pseudo registers - match the users expectations
given the programming model being used. */
+
int rawnum = regno % gdbarch_num_regs (gdbarch);
if (regno < gdbarch_num_regs (gdbarch))
return "";
@@ -1005,7 +1006,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
@@ -1074,21 +1075,29 @@ mips_register_type (struct gdbarch *gdbarch, int regnum)
else if (gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
&& rawnum >= MIPS_FIRST_EMBED_REGNUM
&& rawnum <= MIPS_LAST_EMBED_REGNUM)
- /* The pseudo/cooked view of the embedded registers is always
- 32-bit. The raw view is handled below. */
- return builtin_type (gdbarch)->builtin_int32;
+ {
+ /* The pseudo/cooked view of the embedded registers is always
+ 32-bit. The raw view is handled below. */
+ return builtin_type (gdbarch)->builtin_int32;
+ }
else if (tdep->mips64_transfers_32bit_regs_p)
- /* The target, while possibly using a 64-bit register buffer,
- is only transferring 32-bits of each integer register.
- Reflect this in the cooked/pseudo (ABI) register value. */
- return builtin_type (gdbarch)->builtin_int32;
+ {
+ /* The target, while possibly using a 64-bit register buffer,
+ is only transferring 32-bits of each integer register.
+ Reflect this in the cooked/pseudo (ABI) register value. */
+ return builtin_type (gdbarch)->builtin_int32;
+ }
else if (mips_abi_regsize (gdbarch) == 4)
- /* The ABI is restricted to 32-bit registers (the ISA could be
- 32- or 64-bit). */
- return builtin_type (gdbarch)->builtin_int32;
+ {
+ /* The ABI is restricted to 32-bit registers (the ISA could be
+ 32- or 64-bit). */
+ return builtin_type (gdbarch)->builtin_int32;
+ }
else
- /* 64-bit ABI. */
- return builtin_type (gdbarch)->builtin_int64;
+ {
+ /* 64-bit ABI. */
+ return builtin_type (gdbarch)->builtin_int64;
+ }
}
}
@@ -1236,6 +1245,7 @@ mips_pc_is_mips16 (struct gdbarch *gdbarch, CORE_ADDR memaddr)
elfread.c in the high bit of the info field. Use this to decide
if the function is MIPS16. Otherwise if bit 0 of the address is
set, then ELF file flags will tell if this is a MIPS16 function. */
+
bound_minimal_symbol sym
= lookup_minimal_symbol_by_pc (make_compact_addr (memaddr));
if (sym.minsym)
@@ -1254,6 +1264,7 @@ mips_pc_is_micromips (struct gdbarch *gdbarch, CORE_ADDR memaddr)
if the function is microMIPS. Otherwise if bit 0 of the address
is set, then ELF file flags will tell if this is a microMIPS
function. */
+
bound_minimal_symbol sym
= lookup_minimal_symbol_by_pc (make_compact_addr (memaddr));
if (sym.minsym)
@@ -1273,6 +1284,7 @@ mips_pc_isa (struct gdbarch *gdbarch, CORE_ADDR memaddr)
this to decide if the function is MIPS16 or microMIPS or normal
MIPS. Otherwise if bit 0 of the address is set, then ELF file
flags will tell if this is a MIPS16 or a microMIPS function. */
+
bound_minimal_symbol sym
= lookup_minimal_symbol_by_pc (make_compact_addr (memaddr));
if (sym.minsym)
@@ -1566,7 +1578,7 @@ mips_insn_size (enum mips_isa isa, ULONGEST insn)
else
return MIPS_INSN16_SIZE;
case ISA_MIPS:
- return MIPS_INSN32_SIZE;
+ return MIPS_INSN32_SIZE;
}
internal_error (_("invalid ISA"));
}
@@ -1593,8 +1605,10 @@ mips32_bc1_pc (struct gdbarch *gdbarch, struct regcache *regcache,
int cond;
if (fcsr == -1)
- /* No way to handle; it'll most likely trap anyway. */
- return pc;
+ {
+ /* No way to handle; it'll most likely trap anyway. */
+ return pc;
+ }
fcs = regcache_raw_get_unsigned (regcache, fcsr);
cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01);
@@ -1626,10 +1640,10 @@ is_octeon_bbit_op (int op, struct gdbarch *gdbarch)
{
if (!is_octeon (gdbarch))
return 0;
- /* BBIT0 is encoded as LWC2: 110 010. */
- /* BBIT032 is encoded as LDC2: 110 110. */
- /* BBIT1 is encoded as SWC2: 111 010. */
- /* BBIT132 is encoded as SDC2: 111 110. */
+ /* BBIT0 is encoded as LWC2: 110 010.
+ BBIT032 is encoded as LDC2: 110 110.
+ BBIT1 is encoded as SWC2: 111 010.
+ BBIT132 is encoded as SDC2: 111 110. */
if (op == 50 || op == 54 || op == 58 || op == 62)
return 1;
return 0;
@@ -1647,12 +1661,11 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
int op;
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)
- /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
{
+ /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
switch (op & 0x03)
{
case 0: /* BEQL */
@@ -1668,20 +1681,26 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
}
}
else if (op == 17 && itype_rs (inst) == 8)
- /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
- pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
+ {
+ /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
+ pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
+ }
else if (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);
+ {
+ /* 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)
- /* BC1ANY4F, BC1ANY4T: 010001 01010 xxx0x */
- pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 4);
+ {
+ /* 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. */
{
+ /* JALX: 011101
+ The new PC will be alternate mode. */
unsigned long reg;
reg = jtype_target (inst) << 2;
@@ -1695,28 +1714,33 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
branch_if = op == 58 || op == 62;
bit = itype_rt (inst);
- /* Take into account the *32 instructions. */
if (op == 54 || op == 62)
- bit += 32;
+ {
+ /* Take into account the *32 instructions. */
+ bit += 32;
+ }
if (((regcache_raw_get_signed (regcache,
itype_rs (inst)) >> bit) & 1)
== branch_if)
pc += mips32_relative_offset (inst) + 4;
else
- pc += 8; /* After the delay slot. */
+ {
+ /* After the delay slot. */
+ pc += 8;
+ }
}
else
pc += 4; /* Not a branch, next instruction is easy. */
}
else
- { /* This gets way messy. */
-
- /* Further subdivide into SPECIAL, REGIMM and other. */
+ {
+ /* This gets way messy.
+ Further subdivide into SPECIAL, REGIMM and other. */
switch (op & 0x07) /* Extract bits 28,27,26. */
{
- case 0: /* SPECIAL */
+ case 0: /* SPECIAL */
op = rtype_funct (inst);
switch (op)
{
@@ -1725,7 +1749,7 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
/* Set PC to that address. */
pc = regcache_raw_get_signed (regcache, rtype_rs (inst));
break;
- case 12: /* SYSCALL */
+ case 12: /* SYSCALL */
{
mips_gdbarch_tdep *tdep
= gdbarch_tdep<mips_gdbarch_tdep> (gdbarch);
@@ -1740,10 +1764,10 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
pc += 4;
}
- break; /* end SPECIAL */
- case 1: /* REGIMM */
+ break; /* end SPECIAL */
+ case 1: /* REGIMM */
{
- op = itype_rt (inst); /* branch condition */
+ op = itype_rt (inst); /* branch condition */
switch (op)
{
case 0: /* BLTZ */
@@ -1774,8 +1798,10 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
int dspctl = mips_regnum (gdbarch)->dspctl;
if (dspctl == -1)
- /* No way to handle; it'll most likely trap anyway. */
- break;
+ {
+ /* No way to handle; it'll most likely trap anyway. */
+ break;
+ }
if ((regcache_raw_get_unsigned (regcache,
dspctl) & 0x7f) >= pos)
@@ -1789,7 +1815,7 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
pc += 4;
}
}
- break; /* end REGIMM */
+ break; /* end REGIMM */
case 2: /* J */
case 3: /* JAL */
{
@@ -1823,7 +1849,7 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
break;
case 7:
default:
- greater_branch: /* BGTZ, BGTZL */
+ greater_branch: /* BGTZ, BGTZL */
if (regcache_raw_get_signed (regcache, itype_rs (inst)) > 0)
pc += mips32_relative_offset (inst) + 4;
else
@@ -1889,8 +1915,10 @@ micromips_bc1_pc (struct gdbarch *gdbarch, struct regcache *regcache,
int cond;
if (fcsr == -1)
- /* No way to handle; it'll most likely trap anyway. */
- return pc;
+ {
+ /* No way to handle; it'll most likely trap anyway. */
+ return pc;
+ }
fcs = regcache_raw_get_unsigned (regcache, fcsr);
cond = ((fcs >> 24) & 0xfe) | ((fcs >> 23) & 0x01);
@@ -2004,8 +2032,10 @@ micromips_next_pc (struct regcache *regcache, CORE_ADDR pc)
case 0x14: /* BC2F: bits 010000 10100 xxx00 */
case 0x15: /* BC2T: bits 010000 10101 xxx00 */
if (((insn >> 16) & 0x3) == 0x0)
- /* BC2F, BC2T: don't know how to handle these. */
- break;
+ {
+ /* BC2F, BC2T: don't know how to handle these. */
+ break;
+ }
break;
case 0x1a: /* BPOSGE64: bits 010000 11010 */
@@ -2015,8 +2045,10 @@ micromips_next_pc (struct regcache *regcache, CORE_ADDR pc)
int dspctl = mips_regnum (gdbarch)->dspctl;
if (dspctl == -1)
- /* No way to handle; it'll most likely trap anyway. */
- break;
+ {
+ /* No way to handle; it'll most likely trap anyway. */
+ break;
+ }
if ((regcache_raw_get_unsigned (regcache,
dspctl) & 0x7f) >= pos)
@@ -2046,27 +2078,27 @@ micromips_next_pc (struct regcache *regcache, CORE_ADDR pc)
case 0x1d: /* JALS: bits 011101 */
case 0x35: /* J: bits 110101 */
case 0x3d: /* JAL: bits 111101 */
- pc = ((pc | 0x7fffffe) ^ 0x7fffffe) | (b0s26_imm (insn) << 1);
+ pc = ((pc | 0x7fffffe) ^ 0x7fffffe) | (b0s26_imm (insn) << 1);
break;
case 0x25: /* BEQ: bits 100101 */
- if (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
+ if (regcache_raw_get_signed (regcache, b0s5_reg (insn >> 16))
== regcache_raw_get_signed (regcache, b5s5_reg (insn >> 16)))
- pc += micromips_relative_offset16 (insn);
- else
- pc += micromips_pc_insn_size (gdbarch, pc);
+ pc += micromips_relative_offset16 (insn);
+ 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 += micromips_relative_offset16 (insn);
else
- pc += micromips_pc_insn_size (gdbarch, pc);
+ pc += micromips_pc_insn_size (gdbarch, pc);
break;
case 0x3c: /* JALX: bits 111100 */
- pc = ((pc | 0xfffffff) ^ 0xfffffff) | (b0s26_imm (insn) << 2);
+ pc = ((pc | 0xfffffff) ^ 0xfffffff) | (b0s26_imm (insn) << 2);
break;
}
break;
@@ -2077,11 +2109,15 @@ micromips_next_pc (struct regcache *regcache, CORE_ADDR pc)
{
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));
+ {
+ /* 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);
+ {
+ /* JRADDIUSP: bits 010001 11000 */
+ pc = regcache_raw_get_signed (regcache, MIPS_RA_REGNUM);
+ }
break;
case 0x23: /* BEQZ16: bits 100011 */
@@ -2150,6 +2186,7 @@ enum mips16_inst_fmts
extRi64type, /* 20 5,6,5,5,3,3,5 */
extshift64type /* 21 5,5,1,1,1,1,1,1,5,1,1,1,3,5 */
};
+
/* I am heaping all the fields of the formats into one structure and
then, only the fields which are involved in instruction extension. */
struct upk_mips16
@@ -2159,7 +2196,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. */
@@ -2267,7 +2303,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. */
@@ -2395,6 +2430,7 @@ mips16_next_pc (struct regcache *regcache, CORE_ADDR pc)
It works by decoding the current instruction and predicting where a
branch will go. This isn't hard because all the data is available.
The MIPS32, MIPS16 and microMIPS variants are quite different. */
+
static CORE_ADDR
mips_next_pc (struct regcache *regcache, CORE_ADDR pc)
{
@@ -2482,7 +2518,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
@@ -2517,8 +2552,7 @@ mips16_get_imm (unsigned short prev_inst, /* previous instruction */
}
}
-
-/* Analyze the function prologue from START_PC to LIMIT_PC. Builds
+/* 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. */
@@ -2535,8 +2569,8 @@ mips16_scan_prologue (struct gdbarch *gdbarch,
CORE_ADDR cur_pc;
CORE_ADDR frame_addr = 0; /* Value of $r17, used as frame pointer. */
CORE_ADDR sp;
- long frame_offset = 0; /* Size of stack frame. */
- long frame_adjust = 0; /* Offset of FP from SP. */
+ long frame_offset = 0; /* Size of stack frame. */
+ long frame_adjust = 0; /* Offset of FP from SP. */
int frame_reg = MIPS_SP_REGNUM;
unsigned short prev_inst = 0; /* saved copy of previous instruction. */
unsigned inst = 0; /* current instruction */
@@ -2600,10 +2634,12 @@ mips16_scan_prologue (struct gdbarch *gdbarch,
if (offset < 0) /* Negative stack adjustment? */
frame_offset -= offset;
else
- /* Exit loop if a positive stack adjustment is found, which
- usually means that the stack cleanup code in the function
- epilogue is reached. */
- break;
+ {
+ /* Exit loop if a positive stack adjustment is found, which
+ usually means that the stack cleanup code in the function
+ epilogue is reached. */
+ break;
+ }
}
else if ((inst & 0xf800) == 0xd000) /* sw reg,n($sp) */
{
@@ -2888,7 +2924,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);
@@ -3063,7 +3099,7 @@ micromips_scan_prologue (struct gdbarch *gdbarch,
&& dreg == MIPS_SP_REGNUM && sreg == MIPS_SP_REGNUM
&& treg == 3)
/* (D)SUBU $sp, $v1 */
- sp_adj = v1_off;
+ sp_adj = v1_off;
else if (op != 0x150
/* ADDU: bits 000000 00101010000 */
/* DADDU: bits 010110 00101010000 */
@@ -3273,7 +3309,7 @@ micromips_scan_prologue (struct gdbarch *gdbarch,
gdbarch_num_regs (gdbarch) + frame_reg)
+ frame_offset - frame_adjust);
/* FIXME: brobecker/2004-10-10: Just as in the mips32 case, we should
- be able to get rid of the assignment below, evetually. But it's
+ be able to get rid of the assignment below, evetually. But it's
still needed for now. */
this_cache->saved_regs[gdbarch_num_regs (gdbarch)
+ mips_regnum (gdbarch)->pc]
@@ -3292,7 +3328,7 @@ micromips_scan_prologue (struct gdbarch *gdbarch,
/* Heuristic unwinder for procedures using microMIPS instructions.
Procedures that use the 32-bit instruction set are handled by the
- mips_insn32 unwinder. Likewise MIPS16 and the mips_insn16 unwinder. */
+ mips_insn32 unwinder. Likewise MIPS16 and the mips_insn16 unwinder. */
static struct mips_frame_cache *
mips_micro_frame_cache (const frame_info_ptr &this_frame, void **this_cache)
@@ -3425,7 +3461,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
@@ -3437,7 +3473,7 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
int prev_non_prologue_insn;
int this_non_prologue_insn;
int non_prologue_insns;
- CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for
+ CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for
frame-pointer. */
int prev_delay_slot;
CORE_ADDR prev_pc;
@@ -3499,10 +3535,12 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
if (offset < 0) /* Negative stack adjustment? */
frame_offset -= offset;
else
- /* Exit loop if a positive stack adjustment is found, which
- usually means that the stack cleanup code in the function
- epilogue is reached. */
- break;
+ {
+ /* Exit loop if a positive stack adjustment is found, which
+ usually means that the stack cleanup code in the function
+ epilogue is reached. */
+ break;
+ }
seen_sp_adjust = 1;
}
else if (((high_word & 0xFFE0) == 0xafa0) /* sw reg,offset($sp) */
@@ -3576,7 +3614,7 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
}
}
}
- else if ((high_word & 0xFFE0) == 0xafc0 /* sw reg,offset($30) */
+ else if ((high_word & 0xFFE0) == 0xafc0 /* sw reg,offset($30) */
&& !regsize_is_64_bits)
{
set_reg_offset (gdbarch, this_cache, reg, frame_addr + offset);
@@ -3641,7 +3679,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);
@@ -3660,7 +3698,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. */
@@ -3673,7 +3711,7 @@ mips32_scan_prologue (struct gdbarch *gdbarch,
/* Heuristic unwinder for procedures using 32-bit instructions (covers
both 32-bit and 64-bit MIPS ISAs). Procedures using 16-bit
instructions (a.k.a. MIPS16) are handled by the mips_insn16
- unwinder. Likewise microMIPS and the mips_micro unwinder. */
+ unwinder. Likewise microMIPS and the mips_micro unwinder. */
static struct mips_frame_cache *
mips_insn32_frame_cache (const frame_info_ptr &this_frame, void **this_cache)
@@ -3704,7 +3742,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);
@@ -3904,30 +3942,31 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
mips_gdbarch_tdep *tdep = gdbarch_tdep<mips_gdbarch_tdep> (gdbarch);
if (mips_mask_address_p (tdep) && (((ULONGEST) addr) >> 32 == 0xffffffffUL))
- /* This hack is a work-around for existing boards using PMON, the
- simulator, and any other 64-bit targets that doesn't have true
- 64-bit addressing. On these targets, the upper 32 bits of
- addresses are ignored by the hardware. Thus, the PC or SP are
- likely to have been sign extended to all 1s by instruction
- sequences that load 32-bit addresses. For example, a typical
- piece of code that loads an address is this:
-
- lui $r2, <upper 16 bits>
- ori $r2, <lower 16 bits>
-
- But the lui sign-extends the value such that the upper 32 bits
- may be all 1s. The workaround is simply to mask off these
- bits. In the future, gcc may be changed to support true 64-bit
- addressing, and this masking will have to be disabled. */
- return addr &= 0xffffffffUL;
+ {
+ /* This hack is a work-around for existing boards using PMON, the
+ simulator, and any other 64-bit targets that doesn't have true
+ 64-bit addressing. On these targets, the upper 32 bits of
+ addresses are ignored by the hardware. Thus, the PC or SP are
+ likely to have been sign extended to all 1s by instruction
+ sequences that load 32-bit addresses. For example, a typical
+ piece of code that loads an address is this:
+
+ lui $r2, <upper 16 bits>
+ ori $r2, <lower 16 bits>
+
+ But the lui sign-extends the value such that the upper 32 bits
+ may be all 1s. The workaround is simply to mask off these
+ bits. In the future, gcc may be changed to support true 64-bit
+ addressing, and this masking will have to be disabled. */
+ return addr &= 0xffffffffUL;
+ }
else
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
@@ -3946,7 +3985,7 @@ 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. */
insn = mips_fetch_instruction (gdbarch, ISA_MIPS, loc, NULL);
@@ -3954,7 +3993,7 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
if (itype_op (insn) != LL_OPCODE && itype_op (insn) != LLD_OPCODE)
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)
{
@@ -4130,7 +4169,7 @@ micromips_deal_with_atomic_sequence (struct gdbarch *gdbarch,
case 0x35: /* J: bits 110101 */
case 0x3d: /* JAL: bits 111101 */
case 0x3c: /* JALX: bits 111100 */
- return {}; /* Fall back to the standard single-step code. */
+ return {}; /* Fall back to the standard single-step code. */
case 0x18: /* POOL32C: bits 011000 */
if ((b12s4_op (insn) & 0xb) == 0xb)
@@ -4157,10 +4196,10 @@ micromips_deal_with_atomic_sequence (struct gdbarch *gdbarch,
&& b5s5_op (insn) != 0x18)
/* JRADDIUSP: bits 010001 11000 */
break;
- return {}; /* Fall back to the standard single-step code. */
+ return {}; /* Fall back to the standard single-step code. */
case 0x33: /* B16: bits 110011 */
- return {}; /* Fall back to the standard single-step code. */
+ return {}; /* Fall back to the standard single-step code. */
}
break;
}
@@ -4244,7 +4283,6 @@ mips_about_to_return (struct gdbarch *gdbarch, CORE_ADDR pc)
return (insn & ~hint) == 0x3e00008; /* jr(.hb) $ra */
}
-
/* This fencepost looks highly suspicious to me. Removing it also
seems suspicious as it could affect remote debugging across serial
lines. */
@@ -4476,6 +4514,7 @@ mips_type_needs_double_align (struct type *type)
/* Adjust the address downward (direction of stack growth) so that it
is correctly aligned for a new stack frame. */
+
static CORE_ADDR
mips_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
{
@@ -4817,7 +4856,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)
@@ -4830,7 +4869,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. */
@@ -4838,7 +4877,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)
@@ -4861,7 +4900,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
@@ -6525,7 +6563,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
@@ -6723,7 +6760,7 @@ mips_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
that bound, then use an arbitrary large number as the upper bound. */
limit_pc = skip_prologue_using_sal (gdbarch, pc);
if (limit_pc == 0)
- limit_pc = pc + 100; /* Magic. */
+ limit_pc = pc + 100; /* Magic. */
if (mips_pc_is_mips16 (gdbarch, pc))
return mips16_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL);
@@ -6964,7 +7001,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)
{
@@ -7146,7 +7182,7 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
{
rs = itype_rs (inst);
rt = itype_rt (inst);
- return (is_octeon_bbit_op (op, gdbarch)
+ return (is_octeon_bbit_op (op, gdbarch)
|| op >> 2 == 5 /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
|| op == 29 /* JALX: bits 011101 */
|| (op == 17
@@ -7393,7 +7429,7 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
/* Make sure we don't scan back before the beginning of the current
function, since we may fetch constant data or insns that look like
a jump. Of course we might do that anyway if the compiler has
- moved constants inline. :-( */
+ moved constants inline. :-( */
if (find_pc_partial_function (bpaddr, NULL, &func_addr, NULL)
&& func_addr > boundary && func_addr <= bpaddr)
boundary = func_addr;
@@ -7404,7 +7440,7 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
return bpaddr;
/* If the previous instruction has a branch delay slot, we have
- to move the breakpoint to the branch instruction. */
+ to move the breakpoint to the branch instruction. */
prev_addr = bpaddr - 4;
if (mips32_insn_at_pc_has_delay_slot (gdbarch, prev_addr))
bpaddr = prev_addr;
@@ -7437,36 +7473,42 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
break;
addr -= MIPS_INSN16_SIZE;
if (i == 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 0))
- /* Looks like a JR/JALR at [target-1], but it could be
- the second word of a previous JAL/JALX, so record it
- and check back one more. */
- jmpaddr = addr;
+ {
+ /* Looks like a JR/JALR at [target-1], but it could be
+ the second word of a previous JAL/JALX, so record it
+ and check back one more. */
+ jmpaddr = addr;
+ }
else if (i > 1 && insn_at_pc_has_delay_slot (gdbarch, addr, 1))
{
if (i == 2)
- /* Looks like a JAL/JALX at [target-2], but it could also
- be the second word of a previous JAL/JALX, record it,
- and check back one more. */
- jmpaddr = addr;
+ {
+ /* Looks like a JAL/JALX at [target-2], but it could also
+ be the second word of a previous JAL/JALX, record it,
+ and check back one more. */
+ jmpaddr = addr;
+ }
else
- /* Looks like a JAL/JALX at [target-3], so any previously
- recorded JAL/JALX or JR/JALR must be wrong, because:
-
- >-3: JAL
- -2: JAL-ext (can't be JAL/JALX)
- -1: bdslot (can't be JR/JALR)
- 0: target insn
-
- Of course it could be another JAL-ext which looks
- like a JAL, but in that case we'd have broken out
- of this loop at [target-2]:
-
- -4: JAL
- >-3: JAL-ext
- -2: bdslot (can't be jmp)
- -1: JR/JALR
- 0: target insn */
- jmpaddr = 0;
+ {
+ /* Looks like a JAL/JALX at [target-3], so any previously
+ recorded JAL/JALX or JR/JALR must be wrong, because:
+
+ >-3: JAL
+ -2: JAL-ext (can't be JAL/JALX)
+ -1: bdslot (can't be JR/JALR)
+ 0: target insn
+
+ Of course it could be another JAL-ext which looks
+ like a JAL, but in that case we'd have broken out
+ of this loop at [target-2]:
+
+ -4: JAL
+ >-3: JAL-ext
+ -2: bdslot (can't be jmp)
+ -1: JR/JALR
+ 0: target insn */
+ jmpaddr = 0;
+ }
}
else
{
@@ -7733,15 +7775,19 @@ mips_skip_mips16_trampoline_code (const frame_info_ptr &frame, CORE_ADDR pc)
&& mips_is_stub_suffix (name + prefixlen + 3, 0))
{
if (pc == start_addr)
- /* This is the 'call' part of a call stub. The return
- address is in $2. */
- return get_frame_register_signed
+ {
+ /* This is the 'call' part of a call stub. The return
+ address is in $2. */
+ return get_frame_register_signed
(frame, gdbarch_num_regs (gdbarch) + MIPS_V0_REGNUM);
+ }
else
- /* This is the 'return' part of a call stub. The return
- address is in $18. */
- return get_frame_register_signed
+ {
+ /* This is the 'return' part of a call stub. The return
+ address is in $18. */
+ return get_frame_register_signed
(frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM);
+ }
}
else
return 0; /* Not a stub. */
@@ -7753,15 +7799,19 @@ mips_skip_mips16_trampoline_code (const frame_info_ptr &frame, CORE_ADDR pc)
|| startswith (name, mips_str_call_stub))
{
if (pc == start_addr)
- /* This is the 'call' part of a call stub. Call this helper
- to scan through this code for interesting instructions
- and determine the final PC. */
- return mips_get_mips16_fn_stub_pc (frame, pc);
+ {
+ /* This is the 'call' part of a call stub. Call this helper
+ to scan through this code for interesting instructions
+ and determine the final PC. */
+ return mips_get_mips16_fn_stub_pc (frame, pc);
+ }
else
- /* This is the 'return' part of a call stub. The return address
- is in $18. */
- return get_frame_register_signed
+ {
+ /* This is the 'return' part of a call stub. The return address
+ is in $18. */
+ return get_frame_register_signed
(frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM);
+ }
}
return 0; /* Not a stub. */
@@ -7922,7 +7972,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. */
@@ -7960,7 +8009,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. */
@@ -8361,7 +8409,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 (),
@@ -8832,7 +8879,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;
@@ -8860,7 +8907,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
@@ -8871,7 +8918,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)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v12 2/2] gdb: mips: Add MIPSR6 support
2025-02-19 13:01 [PATCH 0/2] gdb: mips: Add MIPSR6 support Milica Matic
2025-02-19 13:01 ` [PATCH v12 " Milica Matic
2025-02-19 13:01 ` [PATCH v12 1/2] Apply coding guidelines Milica Matic
@ 2025-02-19 13:01 ` Milica Matic
2025-02-25 22:35 ` Maciej W. Rozycki
2 siblings, 1 reply; 9+ messages in thread
From: Milica Matic @ 2025-02-19 13:01 UTC (permalink / raw)
To: gdb-patches
Cc: Milica Matic, Djordje Todorovic, Milos Kalicanin, simark, cfu,
aburgess, kevinb, macro
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.
---
gdb/mips-tdep.c | 548 +++++++++++++--
gdb/testsuite/gdb.arch/mips-64-r6.c | 916 ++++++++++++++++++++++++++
gdb/testsuite/gdb.arch/mips-64-r6.exp | 76 +++
3 files changed, 1498 insertions(+), 42 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 dbf0b1aa5df..49093640e01 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::array_view<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)
@@ -326,6 +330,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
@@ -1553,6 +1568,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)
@@ -1589,6 +1605,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. */
@@ -1649,6 +1683,77 @@ is_octeon_bbit_op (int op, struct gdbarch *gdbarch)
return 0;
}
+/* Detects whether overflow occurs when adding two 32-bit integers. */
+
+static bool
+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 and 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 bool
+is_add64bit_overflow (int64_t a, int64_t b)
+{
+ if (a != (int32_t) a)
+ return true;
+ if (b != (int32_t) b)
+ return true;
+ return is_add32bit_overflow ((int32_t) a, (int32_t) b);
+}
+
+#define DELAY_SLOT_SIZE 4
+
+/* 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);
+ bool taken = false;
+
+ /* 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);
+ }
+
+ 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. */
@@ -1659,11 +1764,16 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
struct gdbarch *gdbarch = regcache->arch ();
unsigned long inst;
int op;
+ bool mips64bitreg = false;
+
+ if (mips_isa_regsize (gdbarch) == 8)
+ mips64bitreg = true;
+
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 (op >> 2 == 5)
+ if (op >> 2 == 5 && ((op & 0x02) == 0 || itype_rt (inst) == 0))
{
/* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
switch (op & 0x03)
@@ -1673,7 +1783,7 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
case 1: /* BNEL */
goto neq_branch;
case 2: /* BLEZL */
- goto less_branch;
+ goto lez_branch;
case 3: /* BGTZL */
goto greater_branch;
default:
@@ -1685,19 +1795,23 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
/* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
}
- else if (op == 17 && itype_rs (inst) == 9
+ 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
+ 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)
+ else if (!is_mipsr6_isa (gdbarch) && op == 29)
{
/* JALX: 011101
The new PC will be alternate mode. */
@@ -1730,7 +1844,148 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
pc += 8;
}
}
+ else if (is_mipsr6_isa (gdbarch))
+ {
+ if (op == 8 || op == 24)
+ {
+ /* BOVC, BEQZALC, BEQC and BNVC, BNEZALC, BNEC */
+ 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);
+ bool taken = false;
+ if (rs >= rt)
+ {
+ /* BOVC (BNVC) */
+ if (mips64bitreg)
+ taken = is_add64bit_overflow (val_rs, val_rt);
+ else
+ taken = is_add32bit_overflow (val_rs, val_rt);
+ }
+ else if (rs < rt && rs == 0)
+ {
+ /* BEQZALC (BNEZALC) */
+ taken = (val_rt == 0);
+ }
+ else
+ {
+ /* BEQC (BNEC) */
+ taken = (val_rs == val_rt);
+ }
+ if (op == 24)
+ {
+ /* BNVC, BNEZALC, BNEC */
+ 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 == 17 && (itype_rs (inst) == 9 || itype_rs (inst) == 13))
+ {
+ /* BC1EQZ, BC1NEZ */
+ 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 *buf_tmp = (gdb_byte *) alloca (sizeof (gdb_byte) * 4);
+ gdb::array_view<gdb_byte> raw_buffer = gdb::make_array_view (buf_tmp, 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[0];
+
+ 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);
+ bool taken = false;
+ /* 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);
+ bool 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
+ {
+ /* Not a branch, next instruction is easy. */
+ pc += 4;
+ }
+ }
else
pc += 4; /* Not a branch, next instruction is easy. */
}
@@ -1774,7 +2029,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
@@ -1790,6 +2044,7 @@ 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)
@@ -1803,11 +2058,18 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
break;
}
+ /* BPOSGE32C */
+ if (op == 0x1d)
+ {
+ if (!is_mipsr6_isa (gdbarch))
+ break;
+ }
+
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 */
@@ -1841,19 +2103,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 */
@@ -2486,6 +2743,63 @@ 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))
+ {
+ case 50: /* BC */
+ case 58: /* BALC */
+ if (is_mipsr6_isa (gdbarch))
+ return 1;
+ break;
+ case 8: /* BOVC, BEQZALC, BEQC */
+ case 24: /* BNVC, BNEZALC, BNEC */
+ if (is_mipsr6_isa (gdbarch))
+ return 2;
+ break;
+ case 54: /* BEQZC, JIC */
+ case 62: /* BNEZC, JIALC */
+ if (is_mipsr6_isa (gdbarch))
+ {
+ /* JIC, JIALC are unconditional */
+ return (itype_rs (insn) == 0) ? 1 : 2;
+ }
+ break;
+ case 22: /* BLEZC, BGEZC, BGEC */
+ case 23: /* BGTZC, BLTZC, BLTC */
+ case 6: /* BLEZALC, BGEZALC, BGEUC */
+ case 7: /* BGTZALC, BLTZALC, BLTUC */
+ if (is_mipsr6_isa (gdbarch)
+ && itype_rt (insn) != 0)
+ return 2;
+ break;
+ case 1: /* BPOSGE32C */
+ 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;
@@ -3529,7 +3843,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? */
@@ -3669,7 +3984,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;
@@ -3975,6 +4292,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)
@@ -3987,10 +4368,11 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
int index;
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"
@@ -4020,28 +4402,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. */
@@ -4049,12 +4475,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;
@@ -4203,7 +4629,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
@@ -4279,8 +4705,14 @@ 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
@@ -6798,7 +7230,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;
}
@@ -7176,6 +7610,7 @@ 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)
@@ -7183,15 +7618,23 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
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 */
+ || (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 */
@@ -7210,7 +7653,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;
}
@@ -7422,7 +7869,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);
@@ -7444,6 +7902,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
{
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..e1a1dd041cc
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/mips-64-r6.c
@@ -0,0 +1,916 @@
+/*
+ Copyright 2023-2025 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;
+}
+
+/* If any test_r6_* function returns non-zero, it's a 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..710da94c542
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/mips-64-r6.exp
@@ -0,0 +1,76 @@
+# Copyright (C) 2023-2025 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
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v12 1/2] Apply coding guidelines
2025-02-19 13:01 ` [PATCH v12 1/2] Apply coding guidelines Milica Matic
@ 2025-02-19 23:21 ` Kevin Buettner
2025-02-23 16:40 ` Maciej W. Rozycki
0 siblings, 1 reply; 9+ messages in thread
From: Kevin Buettner @ 2025-02-19 23:21 UTC (permalink / raw)
To: gdb-patches; +Cc: Milica Matic
On Wed, 19 Feb 2025 13:01:37 +0000
Milica Matic <milica.matic@htecgroup.com> wrote:
> Format mips-tdep.c code as described on links:
> https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards
> https://www.gnu.org/prep/standards/standards.html#Comments
> ---
> gdb/mips-tdep.c | 395 +++++++++++++++++++++++++++---------------------
> 1 file changed, 221 insertions(+), 174 deletions(-)
This is okay.
Approved-by: Kevin Buettner <kevinb@redhat.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v12 1/2] Apply coding guidelines
2025-02-19 23:21 ` Kevin Buettner
@ 2025-02-23 16:40 ` Maciej W. Rozycki
0 siblings, 0 replies; 9+ messages in thread
From: Maciej W. Rozycki @ 2025-02-23 16:40 UTC (permalink / raw)
To: Kevin Buettner, Milica Matic; +Cc: gdb-patches
On Wed, 19 Feb 2025, Kevin Buettner wrote:
> > Format mips-tdep.c code as described on links:
> > https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards
> > https://www.gnu.org/prep/standards/standards.html#Comments
> > ---
> > gdb/mips-tdep.c | 395 +++++++++++++++++++++++++++---------------------
> > 1 file changed, 221 insertions(+), 174 deletions(-)
>
> This is okay.
Kevin, thank you for your review.
However I wasn't happy with some aspects of the proposal, so I have split
it into pieces by the category, making individual commits self-contained,
easy to navigate and consequently obvious to the reader. I'll appreciate
if any future changes of this kind made to the MIPS backend follow this
pattern.
Strictly speaking I reckon our new-line rule for block comments preceding
variable definitions only applies to ones outside functions, but I don't
care either way, so I have retained these changes. I also made a couple
of small adjustments which were missed around the places poked at.
Unlike adding or removing new lines there is ongoing cost to code changes
made as it affects `git blame' output. Therefore I have applied changes
to places with actual style issues, but I decided against making block
statements out of comments along with the single statements that follow
them each in conditional blocks. I think the benefit outweighs the
ongoing cost of their appearance in `git blame' output that requires an
extra step to bypass this noise. I have no objection to using this style
of course with new code or code that has to be otherwise changed anyway.
Last but not least I can see no reason to merge adjacent comments into
single block comments. There is no such requirement and there was often a
reason to do it this way, such as to emphasize separation. So I have not
applied this part either.
I have adjusted commit headings and descriptions accordingly and posted
the changes applied for easier reference.
Milica, thank you for your contribution.
Maciej
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v12 2/2] gdb: mips: Add MIPSR6 support
2025-02-19 13:01 ` [PATCH v12 2/2] gdb: mips: Add MIPSR6 support Milica Matic
@ 2025-02-25 22:35 ` Maciej W. Rozycki
2025-03-02 0:17 ` Maciej W. Rozycki
0 siblings, 1 reply; 9+ messages in thread
From: Maciej W. Rozycki @ 2025-02-25 22:35 UTC (permalink / raw)
To: Milica Matic
Cc: Djordje Todorovic, Milos Kalicanin, Simon Marchi, Chao-ying Fu,
Andrew Burgess, Kevin Buettner, gdb-patches
On Wed, 19 Feb 2025, Milica Matic wrote:
> 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.
I have now skimmed over your change and obviously branch decoding is the
huge update and the most problematic part. At high level I find new code
a little bit hairy and hard to follow in some places, and will go over it
in details double-checking all is in order, and maybe trying to untangle
it a little. I won't be able to do it before this coming weekend though
at which point I'm going on holiday for a fortnight. Please be a little
patient meanwhile.
Also you may have seen my response to an earlier version where I reported
how I tried to do verification against my 74K Malta only to see it run
unreliably. Sadly the 5Kc core card I previously used broke a couple of
years ago and the replacement newer module does not seem to run equally
well (also it's 32-bit only while the 5Kc was 64-bit).
I suspect it just wasn't debugged as thoroughly as the older one, there
are issues with PCI handling both in Linux and even in the YAMON firmware,
and I think this instability in running GDB verification can be attributed
to cache coherency maintenance issues in Linux, which can be hairy. I may
not have the resources available at hand though to get it sorted quickly.
It doesn't appear to me that the 74K core card is at fault as I have two
of them, slightly different and carrying a 74Kc and a 74Kf each, so if it
was owing to a faulty card, then I imagine they wouldn't behave exactly
the same. Also glibc and GCC verification seems more reproducible. The
old 5Kc core card was rock-solid, I used it for 20 years until it failed.
So instead I made an attempt the other day to use alternate test machines
I have in my remote lab, and that also hit obstacles. One of the systems
refused to respond on the serial console. I've seen it before and it is
likely owing to its lithium coin cell powering the configuration memory
having run out of energy, making the system revert to the video console,
which I have no remote access to. So I cannot make use of that machine
for the time being, until the cell has been replaced and configuration
restored.
The other system booted just fine and completed reference GDB testing.
On second iteration however it broke with uncorrectable multiple-bit ECC
errors reported by DRAM making the system unusable. The errors persisted
after a reboot and firmware testing pointed at just one problematic DRAM
module (out of eight installed total). I have seen this before as well
and cleaning contacts and/or reseating the module should clear the issue.
At worst the module can be removed, reducing system RAM from 256MiB down
to 224MiB, which should still be fine for the purpose of this testing.
I have a maintenance visit scheduled to my lab tomorrow, so I'm going to
address these issues. I'll report back and, fingers crossed, I should at
least be able to verify the lack of regressions from your change for older
systems, possibly by the end of this week.
I will appreciate your understanding of my constraints.
Maciej
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v12 2/2] gdb: mips: Add MIPSR6 support
2025-02-25 22:35 ` Maciej W. Rozycki
@ 2025-03-02 0:17 ` Maciej W. Rozycki
2025-03-28 17:09 ` Maciej W. Rozycki
0 siblings, 1 reply; 9+ messages in thread
From: Maciej W. Rozycki @ 2025-03-02 0:17 UTC (permalink / raw)
To: Milica Matic
Cc: Djordje Todorovic, Milos Kalicanin, Simon Marchi, Chao-ying Fu,
Andrew Burgess, Kevin Buettner, Thomas Bogendoerfer, gdb-patches
On Tue, 25 Feb 2025, Maciej W. Rozycki wrote:
> > 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.
>
> I have now skimmed over your change and obviously branch decoding is the
> huge update and the most problematic part. At high level I find new code
> a little bit hairy and hard to follow in some places, and will go over it
> in details double-checking all is in order, and maybe trying to untangle
> it a little. I won't be able to do it before this coming weekend though
> at which point I'm going on holiday for a fortnight. Please be a little
> patient meanwhile.
>
> Also you may have seen my response to an earlier version where I reported
> how I tried to do verification against my 74K Malta only to see it run
> unreliably. Sadly the 5Kc core card I previously used broke a couple of
> years ago and the replacement newer module does not seem to run equally
> well (also it's 32-bit only while the 5Kc was 64-bit).
>
> I suspect it just wasn't debugged as thoroughly as the older one, there
> are issues with PCI handling both in Linux and even in the YAMON firmware,
> and I think this instability in running GDB verification can be attributed
> to cache coherency maintenance issues in Linux, which can be hairy. I may
> not have the resources available at hand though to get it sorted quickly.
>
> It doesn't appear to me that the 74K core card is at fault as I have two
> of them, slightly different and carrying a 74Kc and a 74Kf each, so if it
> was owing to a faulty card, then I imagine they wouldn't behave exactly
> the same. Also glibc and GCC verification seems more reproducible. The
> old 5Kc core card was rock-solid, I used it for 20 years until it failed.
>
> So instead I made an attempt the other day to use alternate test machines
> I have in my remote lab, and that also hit obstacles. One of the systems
> refused to respond on the serial console. I've seen it before and it is
> likely owing to its lithium coin cell powering the configuration memory
> having run out of energy, making the system revert to the video console,
> which I have no remote access to. So I cannot make use of that machine
> for the time being, until the cell has been replaced and configuration
> restored.
>
> The other system booted just fine and completed reference GDB testing.
> On second iteration however it broke with uncorrectable multiple-bit ECC
> errors reported by DRAM making the system unusable. The errors persisted
> after a reboot and firmware testing pointed at just one problematic DRAM
> module (out of eight installed total). I have seen this before as well
> and cleaning contacts and/or reseating the module should clear the issue.
> At worst the module can be removed, reducing system RAM from 256MiB down
> to 224MiB, which should still be fine for the purpose of this testing.
>
> I have a maintenance visit scheduled to my lab tomorrow, so I'm going to
> address these issues. I'll report back and, fingers crossed, I should at
> least be able to verify the lack of regressions from your change for older
> systems, possibly by the end of this week.
Both systems now fixed and results are as follows:
1. The R4400 system suffers from exactly the same testing instability that
the 74Kf Malta does. Tests time out intermittently that otherwise run
successfully almost instantaneously, so it's not that they are on the
verge of timing out and just either miss it or hit. The two systems
are different enough from each other a hardware fault can be surely
ruled out. However they do share the grand MIPS R4k cache architecture
and given that both are overall stable with other loads I'm even more
confident this has something to do with cache coherency in the context
of ptrace(2) calls.
Example result fluctuations with 74Kf Malta:
=== gdb Summary ===
-# of expected passes 102278
-# of unexpected failures 4060
-# of expected failures 80
+# of expected passes 100275
+# of unexpected failures 1697
+# of expected failures 79
# of unknown successes 5
# of known failures 125
-# of unresolved testcases 190
+# of unresolved testcases 145
# of untested testcases 45
-# of unsupported tests 849
-# of duplicate test names 34
+# of unsupported tests 850
+# of duplicate test names 33
Example result fluctuations with R4400SC DECstation:
=== gdb Summary ===
-# of expected passes 94006
-# of unexpected failures 4236
-# of expected failures 77
-# of known failures 111
-# of unresolved testcases 688
-# of untested testcases 51
+# of expected passes 91166
+# of unexpected failures 3140
+# of expected failures 75
+# of unknown successes 4
+# of known failures 91
+# of unresolved testcases 280
+# of untested testcases 50
# of unsupported tests 879
-# of duplicate test names 9
+# of duplicate test names 29
2. Conversely the R3400 system, which was one I meant to use originally,
but could not owing to the flat coin call, provides stable results with
almost no intermittent failures except for the gdb.threads/ subset,
which has never been stable for MIPS/Linux, even 20 years ago, and is
likely a deficiency of implementation outside GDB. This system has the
unique simpler R3k write-through cache architecture, but is otherwise
the same as the R4400 system referred to above, i.e. the difference
between them is just the CPU daughtercard, which further suggests we
have an issue with R4k cache handling.
Example result fluctuations with R3400 DECstation:
=== gdb Summary ===
-# of expected passes 106361
-# of unexpected failures 923
+# of expected passes 106248
+# of unexpected failures 920
# of expected failures 84
# of unknown successes 4
-# of known failures 125
+# of known failures 124
# of unresolved testcases 3
# of untested testcases 47
# of unsupported tests 879
Note the best scores overall despite the system being the most obsolete
of the three.
It's not completely fine though, as with the `call-ar-st' and `varargs'
tests corrupt kernel oopses reliably trigger that follow this pattern:
FP exception in kernel code[#1]:
CPU: 0 PID: 1301 Comm: call-ar-st Not tainted 5.18.0-rc2-00254-gfb649bda6f56-dirty #6
$ 0 : 00000000 00000000 00000000 00000000
$ 4 : 00000000 00000000 00000000 00000000
$ 8 : 00000000 00000000 00000000 00000000
$12 : 00000000 00000000 00000000 00000000
$16 : 00000000 00000000 00000000 00000000
$20 : 00000000 00000000 00000000 00000000
$24 : 00000000 00000000
$28 : 00000000 00000000 00000000 00000000
Hi : 00000000
Lo : 00000000
epc : 00000000 0x0
ra : 00000000 0x0
Status: 00000000
Cause : 00000000 (ExcCode 00)
PrId : 00000230 (R3000A)
Modules linked in:
Process call-ar-st (pid: 1301, threadinfo=649d947d, task=88fb543d, tls=77ffd580)
Stack :
Call Trace:
Code: (Bad address in epc)
---[ end trace 0000000000000000 ]---
Reserved instruction in kernel code[#2]:
CPU: 0 PID: 1301 Comm: call-ar-st Tainted: G D 5.18.0-rc2-00254-gfb649bda6f56-dirty #6
$ 0 : 00000000 1000ec01 00000080 00000000
$ 4 : 81880660 80de4c40 1000ec00 ffff00fe
$ 8 : 344c6906 0000ec00 0000022b ffffffff
$12 : 00000000 00395b10 00000000 656e7265
$16 : 00000001 00010001 00000000 00000000
$20 : 81831668 00000000 00000000 00400000
$24 : 00000000 80f3ded0
$28 : 80e30000 80c0bce0 819a96e0 81a52000
Hi : 005509b3
Lo : 4102d5e4
epc : 81a52084 0x81a52084
ra : 81a52000 0x81a52000
Status: 1008ec04 IEp
Cause : 10000028 (ExcCode 0a)
PrId : 00000230 (R3000A)
Modules linked in:
Process call-ar-st (pid: 1301, threadinfo=649d947d, task=88fb543d, tls=77ffd580)
Stack : 80c01300 8042b188 1000ec01 80c7d430 81a53f00 8018a374 00001142 00000000
00000d06 00000000 00000000 80730000 00000001 00000000 80c7d430 00000020
81a5222c 8042b188 00000000 80c04a00 80c7d430 1000ec01 804e35e0 2abe260a
00000020 8018993c 81a5222c 2abe260a 00000020 8042fdf0 2abe260a 818f7010
80c7d430 00000020 81a5222c 804e35e0 807027c8 80786f60 00011606 80786f60
...
Call Trace:
[<8042b188>] skb_release_data+0x1a8/0x1ec
[<8018a374>] kfree+0xac/0x168
[<8042b188>] skb_release_data+0x1a8/0x1ec
[<804e35e0>] tcp_rcv_established+0x214/0x8ac
[<8018993c>] kmem_cache_free.part.0+0x4c/0xac
[<8042fdf0>] __kfree_skb+0x2c/0x40
[<804e35e0>] tcp_rcv_established+0x214/0x8ac
[<804f2d68>] tcp_v4_do_rcv+0x190/0x250
[<804f5d38>] tcp_v4_rcv+0xf3c/0xfd4
[<800b0654>] timekeeping_update+0x158/0x2dc
[<80138a78>] __pagevec_lru_add+0x3bc/0x3f8
[<8041d888>] sock_pfree+0x0/0x4c
[<80740000>] mitigations_parse_cmdline+0x90/0x98
[<804f4d2c>] tcp_v4_early_demux+0xe4/0x1b4
[<804bc2b0>] ip_protocol_deliver_rcu+0x54/0x2ec
[<804bb70c>] ip_rcv_finish_core.constprop.0+0x18c/0x538
[<800a7b20>] dma_map_page_attrs+0x21c/0x324
[<804bc5fc>] ip_local_deliver+0xb4/0x108
[<80447c90>] __netif_receive_skb_one_core+0x4c/0x5c
[<80447e34>] process_backlog+0x94/0x1bc
[<80448b18>] __napi_poll+0x40/0x1d0
[<80740000>] mitigations_parse_cmdline+0x90/0x98
[<80740000>] mitigations_parse_cmdline+0x90/0x98
[<80448ebc>] net_rx_action+0xf4/0x25c
[<80071a00>] queue_work_on+0x7c/0x84
[<803bdf68>] add_interrupt_randomness+0x24/0x210
[<806032e4>] __do_softirq+0x17c/0x34c
[<8009e494>] handle_level_irq+0xc8/0x208
[<80049180>] die+0x110/0x128
[<80740000>] mitigations_parse_cmdline+0x90/0x98
[<80041cf0>] dec_irq_dispatch+0x10/0x20
[<80043754>] handle_int+0x14c/0x158
Code: 00000000 00000000 00000000 <54002820> 936d6000 00089891 f831262b 0003aaaa 00080000
---[ end trace 0000000000000000 ]---
Fixing recursive fault but reboot is needed!
(mind that the R3k uses an ordinary interrupt to report FP exceptions).
Given the obvious all-zero corruption I suspect bloody `.set noreorder'
piece of code code somewhere missing a load delay slot that has no
valid justification to schedule instructions by hand in the first
place. Needless to say both tests score a lot of failures as a result.
The R3k issue is a bohrbug though and I have a pair of solid reproducers,
so I'll take care of it. I don't have the resources however to chase the
R4k cache coherency issue, so instead I've cc'd Thomas, the MIPS/Linux
kernel maintainer, to raise his awareness of the problem.
This all also means I can use the R3400 system to reliably verify GDB
changes and will proceed with your submission once I'm back from my
holiday in a fortnight's time. Meanwhile I do hope this analysis helps.
Maciej
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v12 2/2] gdb: mips: Add MIPSR6 support
2025-03-02 0:17 ` Maciej W. Rozycki
@ 2025-03-28 17:09 ` Maciej W. Rozycki
0 siblings, 0 replies; 9+ messages in thread
From: Maciej W. Rozycki @ 2025-03-28 17:09 UTC (permalink / raw)
To: Milica Matic
Cc: Djordje Todorovic, Milos Kalicanin, Simon Marchi, Chao-ying Fu,
Andrew Burgess, Kevin Buettner, Thomas Bogendoerfer, gdb-patches
On Sun, 2 Mar 2025, Maciej W. Rozycki wrote:
> This all also means I can use the R3400 system to reliably verify GDB
> changes and will proceed with your submission once I'm back from my
> holiday in a fortnight's time. Meanwhile I do hope this analysis helps.
I have regression-tested your change now and while the problem previously
noted persists with `call-ar-st' and `varargs' tests, leaving them stuck
forever in a weird state:
macro 1299 0.0 0.0 0 0 ? XW Mar24 0:03 [call-ar-st]
macro 4501 0.0 0.0 0 0 ? XW Mar24 0:05 [varargs]
results are looking good otherwise, except for the newly-added R6 case:
Running .../gdb/testsuite/gdb.arch/mips-64-r6.exp ...
gdb compile failed, Assembler messages:
Fatal error: can't create .../gdb/testsuite/gdb.arch/mips-64-r60.o: No such file or directory
DUPLICATE: gdb.arch/mips-64-r6.exp: failed to prepare
This is because there's no output directory present for the object file
requested: there's no gdb.arch/ under .../gdb/testsuite/. I reckon it's
been many years already since our directory arrangement here was changed.
Have you run your verification with the upstream trunk version of GDB?
Also the duplicate failure message needs to be corrected as test messages
are expected to be uniquely identifiable.
Please update your patch accordingly and resubmit; meanwhile I'll have a
detailed look at the backend updates.
Maciej
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-03-28 17:10 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-19 13:01 [PATCH 0/2] gdb: mips: Add MIPSR6 support Milica Matic
2025-02-19 13:01 ` [PATCH v12 " Milica Matic
2025-02-19 13:01 ` [PATCH v12 1/2] Apply coding guidelines Milica Matic
2025-02-19 23:21 ` Kevin Buettner
2025-02-23 16:40 ` Maciej W. Rozycki
2025-02-19 13:01 ` [PATCH v12 2/2] gdb: mips: Add MIPSR6 support Milica Matic
2025-02-25 22:35 ` Maciej W. Rozycki
2025-03-02 0:17 ` Maciej W. Rozycki
2025-03-28 17:09 ` 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