* [PATCH^11] gdb: mips: Add MIPSR6 support
@ 2025-02-10 17:28 Milica Matic
2025-02-11 20:10 ` Kevin Buettner
0 siblings, 1 reply; 5+ messages in thread
From: Milica Matic @ 2025-02-10 17:28 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 v10: Apply suggestions provided by Kevin Buettner.
Additionaly, 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
[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-10
Andrew Bennett <andrew.bennett@imgtec.com>
Matthew Fortune <matthew.fortune@mips.com>
Faraz Shahbazker <fshahbazker@wavecomp.com>
---
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
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index a28c99d3366..49093640e01 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,12 @@ 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. */
+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)
/* The sizes of floating point registers. */
@@ -220,6 +224,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";
@@ -325,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
@@ -633,7 +649,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 +672,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 +683,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 +1021,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 +1090,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 +1260,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 +1279,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 +1299,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)
@@ -1541,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)
@@ -1566,7 +1594,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"));
}
@@ -1577,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. */
@@ -1593,8 +1639,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,15 +1674,86 @@ 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;
}
+/* 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. */
@@ -1645,14 +1764,18 @@ 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 ((inst & 0xe0000000) != 0) /* Not a special, jump or branch instruction. */
{
- if (op >> 2 == 5)
- /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
+ if (op >> 2 == 5 && ((op & 0x02) == 0 || itype_rt (inst) == 0))
{
+ /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
switch (op & 0x03)
{
case 0: /* BEQL */
@@ -1660,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:
@@ -1668,20 +1791,30 @@ 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);
- else if (op == 17 && itype_rs (inst) == 9
+ {
+ /* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
+ pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 1);
+ }
+ else if (!is_mipsr6_isa (gdbarch)
+ && op == 17
+ && itype_rs (inst) == 9
&& (itype_rt (inst) & 2) == 0)
- /* BC1ANY2F, BC1ANY2T: 010001 01001 xxx0x */
- pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 2);
- else if (op == 17 && itype_rs (inst) == 10
+ {
+ /* BC1ANY2F, BC1ANY2T: 010001 01001 xxx0x */
+ pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 2);
+ }
+ else if (!is_mipsr6_isa (gdbarch)
+ && op == 17
+ && itype_rs (inst) == 10
&& (itype_rt (inst) & 2) == 0)
- /* BC1ANY4F, BC1ANY4T: 010001 01010 xxx0x */
- pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 4);
- else if (op == 29)
- /* JALX: 011101 */
- /* The new PC will be alternate mode. */
{
+ /* BC1ANY4F, BC1ANY4T: 010001 01010 xxx0x */
+ pc = mips32_bc1_pc (gdbarch, regcache, inst, pc + 4, 4);
+ }
+ else if (!is_mipsr6_isa (gdbarch) && op == 29)
+ {
+ /* JALX: 011101
+ The new PC will be alternate mode. */
unsigned long reg;
reg = jtype_target (inst) << 2;
@@ -1695,28 +1828,174 @@ 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 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. */
}
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 +2004,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,17 +2019,16 @@ 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 */
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
@@ -1766,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)
@@ -1774,14 +2053,23 @@ 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;
+ }
+
+ /* 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 */
@@ -1789,7 +2077,7 @@ mips32_next_pc (struct regcache *regcache, CORE_ADDR pc)
pc += 4;
}
}
- break; /* end REGIMM */
+ break; /* end REGIMM */
case 2: /* J */
case 3: /* JAL */
{
@@ -1815,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 */
@@ -1889,8 +2172,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 +2289,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 +2302,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 +2335,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 +2366,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 +2443,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 +2453,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 +2560,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 +2687,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)
{
@@ -2450,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;
@@ -2482,7 +2832,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 +2866,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 +2883,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 +2948,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 +3238,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 +3413,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 +3623,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 +3642,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 +3775,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 +3787,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;
@@ -3493,16 +3843,19 @@ 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? */
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 +3929,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);
@@ -3631,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;
@@ -3641,7 +3996,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 +4015,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 +4028,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 +4059,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 +4259,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
@@ -3936,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)
@@ -3946,15 +4366,16 @@ mips_deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
ULONGEST insn;
int insn_count;
int index;
- int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
+ int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
const int atomic_sequence_length = 16; /* Instruction sequence length. */
+ bool is_mipsr6 = is_mipsr6_isa (gdbarch);
insn = mips_fetch_instruction (gdbarch, ISA_MIPS, loc, NULL);
/* Assume all atomic sequences start with a ll/lld instruction. */
- if (itype_op (insn) != LL_OPCODE && itype_op (insn) != LLD_OPCODE)
+ if (!is_ll_insn (gdbarch, insn))
return {};
- /* Assume that no atomic sequence is longer than "atomic_sequence_length"
+ /* Assume that no atomic sequence is longer than "atomic_sequence_length"
instructions. */
for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
{
@@ -3981,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. */
@@ -4010,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;
@@ -4130,7 +4595,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,14 +4622,14 @@ 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;
}
- if (is_branch)
+ if (is_branch != 0)
{
if (last_breakpoint >= 1)
return {}; /* More than one branch found, fallback to the
@@ -4240,10 +4705,15 @@ mips_about_to_return (struct gdbarch *gdbarch, CORE_ADDR pc)
gdb_assert (mips_pc_is_mips (pc));
insn = mips_fetch_instruction (gdbarch, ISA_MIPS, pc, NULL);
- hint = 0x7c0;
- return (insn & ~hint) == 0x3e00008; /* jr(.hb) $ra */
-}
+ /* Mask the hint and the jalr/jr bit. */
+ hint = 0x7c1;
+
+ if (is_mipsr6_isa (gdbarch) && insn == 0xd81f0000) /* jrc $31 */
+ return 1;
+ /* jr(.hb) $ra and "jalr(.hb) $ra" */
+ return ((insn & ~hint) == 0x3e00008);
+}
/* This fencepost looks highly suspicious to me. Removing it also
seems suspicious as it could affect remote debugging across serial
@@ -4476,6 +4946,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 +5288,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 +5301,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 +5309,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 +5332,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 +6995,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 +7192,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);
@@ -6761,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;
}
@@ -6964,7 +7435,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)
{
@@ -7140,22 +7610,31 @@ mips32_instruction_has_delay_slot (struct gdbarch *gdbarch, ULONGEST inst)
int op;
int rs;
int rt;
+ bool is_mipsr6 = is_mipsr6_isa (gdbarch);
op = itype_op (inst);
if ((inst & 0xe0000000) != 0)
{
rs = itype_rs (inst);
rt = itype_rt (inst);
- return (is_octeon_bbit_op (op, gdbarch)
- || op >> 2 == 5 /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
- || op == 29 /* JALX: bits 011101 */
+ return (is_octeon_bbit_op (op, gdbarch)
+ || (op >> 1 == 10) /* BEQL, BNEL: bits 01010x */
+ || (op >> 1 == 11 && rt == 0) /* BLEZL, BGTZL: bits 01011x */
+ || (!is_mipsr6 && op == 29) /* JALX: bits 011101 */
|| (op == 17
&& (rs == 8
/* BC1F, BC1FL, BC1T, BC1TL: 010001 01000 */
- || (rs == 9 && (rt & 0x2) == 0)
+ || (!is_mipsr6 && rs == 9 && (rt & 0x2) == 0)
/* BC1ANY2F, BC1ANY2T: bits 010001 01001 */
- || (rs == 10 && (rt & 0x2) == 0))));
+ || (!is_mipsr6 && rs == 10 && (rt & 0x2) == 0)))
/* BC1ANY4F, BC1ANY4T: bits 010001 01010 */
+ || (is_mipsr6
+ && ((op == 17
+ && (rs == 9 /* BC1EQZ: 010001 01001 */
+ || rs == 13)) /* BC1NEZ: 010001 01101 */
+ || (op == 18
+ && (rs == 9 /* BC2EQZ: 010010 01001 */
+ || rs == 13))))); /* BC2NEZ: 010010 01101 */
}
else
switch (op & 0x07) /* extract bits 28,27,26 */
@@ -7174,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;
}
@@ -7386,14 +7869,25 @@ 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);
/* 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,10 +7898,16 @@ 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;
+ /* 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
{
@@ -7437,36 +7937,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 +8239,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 +8263,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 +8436,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 +8473,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 +8873,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 +9343,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 +9371,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 +9382,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)
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] 5+ messages in thread* Re: [PATCH^11] gdb: mips: Add MIPSR6 support
2025-02-10 17:28 [PATCH^11] gdb: mips: Add MIPSR6 support Milica Matic
@ 2025-02-11 20:10 ` Kevin Buettner
2025-02-11 20:45 ` Maciej W. Rozycki
0 siblings, 1 reply; 5+ messages in thread
From: Kevin Buettner @ 2025-02-11 20:10 UTC (permalink / raw)
To: Milica Matic
Cc: gdb-patches, Djordje Todorovic, Milos Kalicanin, simark, cfu,
aburgess, macro
Hi Milica,
On Mon, 10 Feb 2025 17:28:12 +0000
Milica Matic <milica.matic@htecgroup.com> 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.
>
> Changes from v10: Apply suggestions provided by Kevin Buettner.
> Additionaly, 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
>
> [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-10
> Andrew Bennett <andrew.bennett@imgtec.com>
> Matthew Fortune <matthew.fortune@mips.com>
> Faraz Shahbazker <fshahbazker@wavecomp.com>
The code formatting looks good to me now.
Additionally, I don't see anything obviously wrong with the MIPSR6
support that's been added. That said, I'm not especially
knowledgeable about MIPS these days, so I ask that you wait a few days
for other maintainers to weigh in before pushing this patch.
Approved-by: Kevin Buettner <kevinb@redhat.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH^11] gdb: mips: Add MIPSR6 support
2025-02-11 20:10 ` Kevin Buettner
@ 2025-02-11 20:45 ` Maciej W. Rozycki
2025-02-19 13:05 ` Milica Matic
2025-02-19 13:14 ` Milica Matic
0 siblings, 2 replies; 5+ messages in thread
From: Maciej W. Rozycki @ 2025-02-11 20:45 UTC (permalink / raw)
To: Kevin Buettner, Milica Matic
Cc: gdb-patches, Djordje Todorovic, Milos Kalicanin, simark, cfu, aburgess
On Tue, 11 Feb 2025, Kevin Buettner wrote:
> > Changes from v10: Apply suggestions provided by Kevin Buettner.
> > Additionaly, 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
> >
> > [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-10
> > Andrew Bennett <andrew.bennett@imgtec.com>
> > Matthew Fortune <matthew.fortune@mips.com>
> > Faraz Shahbazker <fshahbazker@wavecomp.com>
>
> The code formatting looks good to me now.
I have a concern about formatting changes to lines otherwise not affected
by MIPSr6 additions, as they obfuscate the actual code change and would do
so forever if pushed even if you verified that they are legitimate.
Milica, can you therefore please filter them out (`diff -w' should help;
there shouldn't be much if anything to fix up afterwards) and submit as a
separate patch, perhaps a preparatory one? I know this is boring stuff,
but we need to care about our sanity while handling this stuff long-term.
> Additionally, I don't see anything obviously wrong with the MIPSR6
> support that's been added. That said, I'm not especially
> knowledgeable about MIPS these days, so I ask that you wait a few days
> for other maintainers to weigh in before pushing this patch.
I'd like to have a look yet at the refreshed change as I've spotted some
suspicious stuff I need to feel comfortable about before moving forward.
Hopefully with the formatting noise moved out of the way it should be more
straightforward for me to verify things.
Thank you, Kevin, for taking the lead here.
Maciej
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH^11] gdb: mips: Add MIPSR6 support
2025-02-11 20:45 ` Maciej W. Rozycki
@ 2025-02-19 13:05 ` Milica Matic
2025-02-19 13:14 ` Milica Matic
1 sibling, 0 replies; 5+ messages in thread
From: Milica Matic @ 2025-02-19 13:05 UTC (permalink / raw)
To: Maciej W. Rozycki, Kevin Buettner
Cc: gdb-patches, Djordje Todorovic, Milos Kalicanin, simark, cfu, aburgess
HTEC Proprietary
Hello Kevin and Maciej,
I've just separated the patch into 'apply coding guidelines' and 'gdb: add mipsr6 support'.
Many thanks for your suggestions,
Milica
________________________________________
From: Maciej W. Rozycki <macro@orcam.me.uk>
Sent: Tuesday, February 11, 2025 21:45
To: Kevin Buettner; Milica Matic
Cc: gdb-patches@sourceware.org; Djordje Todorovic; Milos Kalicanin; simark@simark.ca; cfu@wavecomp.com; aburgess@redhat.com
Subject: Re: [PATCH^11] gdb: mips: Add MIPSR6 support
CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
On Tue, 11 Feb 2025, Kevin Buettner wrote:
> > Changes from v10: Apply suggestions provided by Kevin Buettner.
> > Additionaly, 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
> >
> > [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-10
> > Andrew Bennett <andrew.bennett@imgtec.com>
> > Matthew Fortune <matthew.fortune@mips.com>
> > Faraz Shahbazker <fshahbazker@wavecomp.com>
>
> The code formatting looks good to me now.
I have a concern about formatting changes to lines otherwise not affected
by MIPSr6 additions, as they obfuscate the actual code change and would do
so forever if pushed even if you verified that they are legitimate.
Milica, can you therefore please filter them out (`diff -w' should help;
there shouldn't be much if anything to fix up afterwards) and submit as a
separate patch, perhaps a preparatory one? I know this is boring stuff,
but we need to care about our sanity while handling this stuff long-term.
> Additionally, I don't see anything obviously wrong with the MIPSR6
> support that's been added. That said, I'm not especially
> knowledgeable about MIPS these days, so I ask that you wait a few days
> for other maintainers to weigh in before pushing this patch.
I'd like to have a look yet at the refreshed change as I've spotted some
suspicious stuff I need to feel comfortable about before moving forward.
Hopefully with the formatting noise moved out of the way it should be more
straightforward for me to verify things.
Thank you, Kevin, for taking the lead here.
Maciej
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH^11] gdb: mips: Add MIPSR6 support
2025-02-11 20:45 ` Maciej W. Rozycki
2025-02-19 13:05 ` Milica Matic
@ 2025-02-19 13:14 ` Milica Matic
1 sibling, 0 replies; 5+ messages in thread
From: Milica Matic @ 2025-02-19 13:14 UTC (permalink / raw)
To: Maciej W. Rozycki, Kevin Buettner
Cc: gdb-patches, Djordje Todorovic, Milos Kalicanin, simark, cfu, aburgess
HTEC Public
Hello Kevin and Maciej,
I've just separated the patch into 'apply coding guidelines' and 'gdb: add mipsr6 support'.
Many thanks for your suggestions,
Milica
________________________________________
From: Maciej W. Rozycki <macro@orcam.me.uk>
Sent: Tuesday, February 11, 2025 21:45
To: Kevin Buettner; Milica Matic
Cc: gdb-patches@sourceware.org; Djordje Todorovic; Milos Kalicanin; simark@simark.ca; cfu@wavecomp.com; aburgess@redhat.com
Subject: Re: [PATCH^11] gdb: mips: Add MIPSR6 support
CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
On Tue, 11 Feb 2025, Kevin Buettner wrote:
> > Changes from v10: Apply suggestions provided by Kevin Buettner.
> > Additionaly, 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
> >
> > [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-10
> > Andrew Bennett <andrew.bennett@imgtec.com>
> > Matthew Fortune <matthew.fortune@mips.com>
> > Faraz Shahbazker <fshahbazker@wavecomp.com>
>
> The code formatting looks good to me now.
I have a concern about formatting changes to lines otherwise not affected
by MIPSr6 additions, as they obfuscate the actual code change and would do
so forever if pushed even if you verified that they are legitimate.
Milica, can you therefore please filter them out (`diff -w' should help;
there shouldn't be much if anything to fix up afterwards) and submit as a
separate patch, perhaps a preparatory one? I know this is boring stuff,
but we need to care about our sanity while handling this stuff long-term.
> Additionally, I don't see anything obviously wrong with the MIPSR6
> support that's been added. That said, I'm not especially
> knowledgeable about MIPS these days, so I ask that you wait a few days
> for other maintainers to weigh in before pushing this patch.
I'd like to have a look yet at the refreshed change as I've spotted some
suspicious stuff I need to feel comfortable about before moving forward.
Hopefully with the formatting noise moved out of the way it should be more
straightforward for me to verify things.
Thank you, Kevin, for taking the lead here.
Maciej
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-02-19 13:15 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-10 17:28 [PATCH^11] gdb: mips: Add MIPSR6 support Milica Matic
2025-02-11 20:10 ` Kevin Buettner
2025-02-11 20:45 ` Maciej W. Rozycki
2025-02-19 13:05 ` Milica Matic
2025-02-19 13:14 ` Milica Matic
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox