[PATCH 4/5] arc: Add disassembler helper

Anton Kolesov
2017-02-14T10:01:52Z

Add disassembler helper for GDB, that uses opcodes structure arc_instruction
and adds convenience functions to handle instruction operands. This interface
solves at least those problems with arc_instruction:

* Some instructions, like "push_s", have implicit operands which are not
directly present in arc_instruction.
* Operands of particular meaning, like branch/jump targets, have various
locations and meaning depending on type of branch/target.
* Access to operand value is abstracted into a separate function, so callee
code shouldn't bother if operand value is an immediate value or in a
register.

Testcases included in this commit are fairly limited - they test exclusively
branch instructions, something that will be used in software single stepping.
Most of the other parts of this disassembler helper are tested during prologue
analysis testing.

gdb/ChangeLog:

yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com>

* configure.tgt: Add arc-insn.o.
* arc-tdep.c (arc_delayed_print_insn): Make non-static.
(dump_arc_instruction_command): New function.
* arc-tdep.h (arc_delayed_print_insn): Add function declaration.
* arch/arc-insn.c: New file.
* arch/arc-insn.h: Likewise.

gdb/doc/ChangeLog:

yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com>

* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".

gdb/testsuite/ChangeLog:

yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com>

* gdb.arch/arc-decode-insn.S: New file.
* gdb.arch/arc-decode-insn.exp: Likewise.
---
gdb/arc-tdep.c | 28 +-
gdb/arc-tdep.h | 6 +
gdb/arch/arc-insn.c | 275 ++++++++
gdb/arch/arc-insn.h | 64 ++
gdb/configure.tgt | 4 +-
gdb/doc/gdb.texinfo | 4 +
gdb/testsuite/gdb.arch/arc-decode-insn.S | 1002 ++++++++++++++++++++++++++++
gdb/testsuite/gdb.arch/arc-decode-insn.exp | 136 ++++
8 files changed, 1516 insertions(+), 3 deletions(-)
create mode 100644 gdb/arch/arc-insn.c
create mode 100644 gdb/arch/arc-insn.h
create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.S
create mode 100644 gdb/testsuite/gdb.arch/arc-decode-insn.exp

diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 5495f2e..a18f16a 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -33,6 +33,7 @@
/* ARC header files. */
#include "opcode/arc.h"
#include "arc-tdep.h"
+#include "arch/arc-insn.h"

/* Standard headers. */
#include <algorithm>
@@ -701,7 +702,7 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
that will not print, or `stream` should be different from standard
gdb_stdlog. */

-static int
+int
arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
{
int (*print_insn) (bfd_vma, struct disassemble_info *);
@@ -1330,6 +1331,26 @@ maintenance_print_arc_command (char *args, int from_tty)
cmd_show_list (maintenance_print_arc_list, from_tty, "");
}

+/* This command accepts single argument - address of instruction to
+ disassemble. */
+
+static void
+dump_arc_instruction_command (char *args, int from_tty)
+{
+ struct value *val;
+ if (args != NULL && strlen (args) > 0)
+ val = evaluate_expression (parse_expression (args).get ());
+ else
+ val = access_value_history (0);
+ record_latest_value (val);
+
+ CORE_ADDR address = value_as_address (val);
+ struct arc_instruction insn;
+ struct disassemble_info di = arc_disassemble_info (target_gdbarch ());
+ arc_insn_decode (address, &di, arc_delayed_print_insn, &insn);
+ arc_insn_dump (insn);
+}
+
/* Suppress warning from -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_arc_tdep;

@@ -1350,6 +1371,11 @@ _initialize_arc_tdep (void)
&maintenance_print_arc_list, "maintenance print arc ", 0,
&maintenanceprintlist);

+ add_cmd ("arc-instruction", class_maintenance,
+ dump_arc_instruction_command,
+ _("Dump arc_instruction structure for specified address."),
+ &maintenance_print_arc_list);
+
/* Debug internals for ARC GDB. */
add_setshow_zinteger_cmd ("arc", class_maintenance,
&arc_debug,
diff --git a/gdb/arc-tdep.h b/gdb/arc-tdep.h
index 326f486..db7647d 100644
--- a/gdb/arc-tdep.h
+++ b/gdb/arc-tdep.h
@@ -123,4 +123,10 @@ arc_mach_is_arcv2 (struct gdbarch *gdbarch)
return gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arcv2;
}

+/* Function to access ARC disassembler. Underlying opcodes disassembler will
+ print an instruction into stream specified in the INFO, so if it is
+ undesired, then this stream should be set to some invisible stream, but it
+ can't be set to an actual NULL value - that would cause a crash. */
+int arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info);
+
#endif /* ARC_TDEP_H */
diff --git a/gdb/arch/arc-insn.c b/gdb/arch/arc-insn.c
new file mode 100644
index 0000000..2dc99a7
--- /dev/null
+++ b/gdb/arch/arc-insn.c
@@ -0,0 +1,275 @@
+/* ARC disassembler helper.
+
+ Copyright 2017 Free Software Foundation, Inc.
+ Contributed by Synopsys 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/>. */
+
+/* GDB header files. */
+#include "defs.h"
+#include "disasm.h"
+
+/* ARC header files. */
+#include "opcode/arc.h"
+#include "arc-tdep.h"
+#include "arch/arc-insn.h"
+
+void
+arc_insn_dump (const struct arc_instruction &insn)
+{
+ struct gdbarch *gdbarch = target_gdbarch ();
+
+ arc_print ("Dumping arc_instruction at %s\n",
+ paddress (gdbarch, insn.address));
+ arc_print ("\tlength=%u\n", insn.length);
+
+ if (!insn.valid)
+ {
+ arc_print ("\tThis is not a valid ARC instruction.\n");
+ return;
+ }
+
+ arc_print ("\tlength_with_limm=%u\n", insn.length + (insn.limm_p ? 4 : 0));
+ arc_print ("\tcc=0x%x\n", insn.condition_code);
+ arc_print ("\tinsn_class=%u\n", insn.insn_class);
+ arc_print ("\tis_control_flow=%i\n", insn.is_control_flow);
+ arc_print ("\thas_delay_slot=%i\n", insn.has_delay_slot);
+
+ CORE_ADDR next_pc = arc_insn_get_linear_next_pc (insn);
+ arc_print ("\tlinear_next_pc=%s\n", paddress (gdbarch, next_pc));
+
+ if (insn.is_control_flow)
+ {
+ CORE_ADDR t = arc_insn_get_branch_target (insn);
+ arc_print ("\tbranch_target=%s\n", paddress (gdbarch, t));
+ }
+
+ arc_print ("\tlimm_p=%i\n", insn.limm_p);
+ if (insn.limm_p)
+ arc_print ("\tlimm_value=0x%08x\n", insn.limm_value);
+
+ if (insn.insn_class == STORE || insn.insn_class == LOAD
+ || insn.insn_class == PUSH || insn.insn_class == POP)
+ {
+ arc_print ("\twriteback_mode=%u\n", insn.writeback_mode);
+ arc_print ("\tdata_size_mode=%u\n", insn.data_size_mode);
+ arc_print ("\tmemory_base_register=%s\n",
+ gdbarch_register_name (gdbarch,
+ arc_insn_get_memory_base_reg (insn)));
+ /* get_memory_offset returns an unsigned CORE_ADDR, but treat it as a
+ LONGEST for a nicer representation. */
+ arc_print ("\taddr_offset=%s\n",
+ plongest (arc_insn_get_memory_offset (insn)));
+ }
+
+ arc_print ("\toperands_count=%u\n", insn.operands_count);
+ for (unsigned int i = 0; i < insn.operands_count; ++i)
+ {
+ int is_reg = (insn.operands[i].kind == ARC_OPERAND_KIND_REG);
+
+ arc_print ("\toperand[%u] = {\n", i);
+ arc_print ("\t\tis_reg=%i\n", is_reg);
+ if (is_reg)
+ arc_print ("\t\tregister=%s\n",
+ gdbarch_register_name (gdbarch, insn.operands[i].value));
+ /* Don't know if this value is signed or not, so print both
+ representations. This tends to look quite ugly, especially for big
+ numbers. */
+ arc_print ("\t\tunsigned value=%s\n",
+ pulongest (arc_insn_get_operand_value (insn, i)));
+ arc_print ("\t\tsigned value=%s\n",
+ plongest (arc_insn_get_operand_value_signed (insn, i)));
+ arc_print ("\t}\n");
+ }
+}
+
+CORE_ADDR
+arc_insn_get_linear_next_pc (const struct arc_instruction &insn)
+{
+ /* In ARC long immediate is always 4 bytes. */
+ return (insn.address + insn.length + (insn.limm_p ? 4 : 0));
+}
+
+CORE_ADDR
+arc_insn_get_branch_target (const struct arc_instruction &insn)
+{
+ gdb_assert (insn.is_control_flow);
+
+ /* BI [c]: PC = nextPC + (c << 2). */
+ if (insn.insn_class == BI)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 2);
+ }
+ /* BIH [c]: PC = nextPC + (c << 1). */
+ else if (insn.insn_class == BIH)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 1);
+ }
+ /* JLI and EI. */
+ /* JLI and EI depend on optional AUX registers. Not supported right now. */
+ else if (insn.insn_class == JLI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "JLI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ else if (insn.insn_class == EI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "EI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ /* LEAVE_S: PC = BLINK. */
+ else if (insn.insn_class == LEAVE)
+ {
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache, ARC_BLINK_REGNUM, &value);
+ return value;
+ }
+ /* BBIT0/1, BRcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BBIT0 || insn.insn_class == BBIT1
+ || insn.insn_class == BRCC)
+ {
+ /* Most instructions has branch target as their sole argument. However
+ conditional brcc/bbit has it as a third operand. */
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 2);
+
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
+ {
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 0);
+
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* J, Jcc, JL, JLcc: PC = operand. */
+ else if (insn.insn_class == JUMP)
+ {
+ /* All jumps are single-operand. */
+ return arc_insn_get_operand_value (insn, 0);
+ }
+
+ /* This is some new and unknown instruction. */
+ gdb_assert_not_reached ("Unknown branch instruction.");
+}
+
+ULONGEST
+arc_insn_get_operand_value (const struct arc_instruction &insn,
+ unsigned int operand_num)
+{
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ return insn.limm_value;
+ case ARC_OPERAND_KIND_SHIMM:
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
+}
+
+LONGEST
+arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+ unsigned int operand_num)
+{
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ /* Convert unsigned raw value to signed one. This assumes 2's
+ complement arithmetic, but so is the LONG_MIN value from generic
+ defs.h and that assumption is true for ARC. */
+ gdb_static_assert (sizeof (insn.limm_value) == sizeof (int));
+ return (((LONGEST) insn.limm_value) ^ INT_MIN) - INT_MIN;
+ case ARC_OPERAND_KIND_SHIMM:
+ /* Sign conversion has been done by binutils. */
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ LONGEST value;
+ regcache_cooked_read_signed (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
+}
+
+int
+arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
+{
+ /* POP_S and PUSH_S have SP as an implicit argument in a disassembler. */
+ if (insn.insn_class == PUSH || insn.insn_class == POP)
+ return ARC_SP_REGNUM;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ gdb_assert (insn.operands_count >= 2);
+ return insn.operands[1].value;
+}
+
+CORE_ADDR
+arc_insn_get_memory_offset (const struct arc_instruction &insn)
+{
+ /* POP_S and PUSH_S have offset as an implicit argument in a
+ disassembler. */
+ if (insn.insn_class == POP)
+ return 4;
+ else if (insn.insn_class == PUSH)
+ return -4;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ if (insn.operands_count < 3)
+ return 0;
+
+ CORE_ADDR value = arc_insn_get_operand_value (insn, 2);
+ /* Handle scaling. */
+ if (insn.writeback_mode == ARC_WRITEBACK_AS)
+ {
+ /* Byte data size is not valid for AS. Halfword means shift by 1 bit.
+ Word and double word means shift by 2 bits. */
+ gdb_assert (insn.data_size_mode != ARC_SCALING_B);
+ if (insn.data_size_mode == ARC_SCALING_H)
+ value <<= 1;
+ else
+ value <<= 2;
+ }
+ return value;
+}
diff --git a/gdb/arch/arc-insn.h b/gdb/arch/arc-insn.h
new file mode 100644
index 0000000..e25d226
--- /dev/null
+++ b/gdb/arch/arc-insn.h
@@ -0,0 +1,64 @@
+/* ARC disassembler helper.
+
+ Copyright 2017 Free Software Foundation, Inc.
+ Contributed by Synopsys 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/>. */
+
+#ifndef ARC_INSN_H
+#define ARC_INSN_H
+
+#include "opcodes/arc-dis.h"
+
+/* Dump INSN into gdb_stdlog. */
+
+void arc_insn_dump (const struct arc_instruction &insn);
+
+/* Get address of next instruction after INSN, assuming linear execution (no
+ taken branches). If instruction has a delay slot, then returned value will
+ point at the instruction in delay slot. That is - "address of instruction +
+ instruction length with LIMM". */
+
+CORE_ADDR arc_insn_get_linear_next_pc (const struct arc_instruction &insn);
+
+/* Get branch/jump target address for the INSN. Note that this function
+ returns branch target and doesn't evaluate if this branch is taken or not.
+ For the indirect jumps value depends in register state, hence can change.
+ It is an error to call this function for a non-branch instruction. */
+
+CORE_ADDR arc_insn_get_branch_target (const struct arc_instruction &insn);
+
+/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
+ For relative branch instructions returned value is an offset, not an actual
+ branch target. */
+
+ULONGEST arc_insn_get_operand_value (const struct arc_instruction &insn,
+ unsigned int operand_num);
+
+/* Like arc_insn_get_operand_value, but returns a signed value. */
+
+LONGEST arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+ unsigned int operand_num);
+
+/* Get register with base address of memory operation. */
+
+int arc_insn_get_memory_base_reg (const struct arc_instruction &insn);
+
+/* Get offset of a memory operation INSN. */
+
+CORE_ADDR arc_insn_get_memory_offset (const struct arc_instruction &insn);
+
+#endif /* ARC_INSN_H */
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index cb909e7..532ac54 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -86,12 +86,12 @@ am33_2.0*-*-linux*)

arc*-*-elf32)
# Target: baremetal ARC elf32 (newlib) target
- gdb_target_obs="arc-newlib-tdep.o arc-tdep.o"
+ gdb_target_obs="arc-insn.o arc-newlib-tdep.o arc-tdep.o"
;;

arc*-*-*)
# Target: Unidentified ARC target
- gdb_target_obs="arc-tdep.o"
+ gdb_target_obs="arc-insn.o arc-tdep.o"
;;

arm*-wince-pe | arm*-*-mingw32ce*)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f619470..8cb656a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22084,6 +22084,10 @@ messages.
@kindex show debug arc
Show the level of ARC specific debugging in operation.

+@item maint print arc arc-instruction @var{address}
+@kindex maint print arc arc-instruction
+Print internal disassembler information about instruction at a given address.
+
@end table

@node ARM
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.S b/gdb/testsuite/gdb.arch/arc-decode-insn.S
new file mode 100644
index 0000000..030f158
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.S
@@ -0,0 +1,1002 @@
+; This testcase is part of GDB, the GNU debugger.
+
+; Copyright 2017 Synopsys 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/>.
+
+.section .text
+.global main
+
+#define TEST_J
+#define TEST_JCC
+#define TEST_JL
+#define TEST_JLCC
+#define TEST_B
+#define TEST_BBIT
+#define TEST_BCC
+#define TEST_BI
+#define TEST_BL
+#define TEST_BRCC
+#define TEST_JLI
+#define TEST_LEAVE_S
+#define TEST_LPCC
+
+; JLI-specific stuff
+#ifdef TEST_JLI
+jli_table:
+ .word 0xdeadbeea
+ .word 0xdeadbeea
+jli_target:
+ .word 0xdeadbeea
+ .word 0xdeadbeea
+
+.set jli_offset, 3
+#endif
+main:
+
+; Each test case requires several symbols to be set, that identify expected
+; parameters of this instruction. Required symbols:
+; ${test}_start: symbol points to start of the test
+; ${test}_end: symbol points to the instruction after the jump/branch
+; instruction.
+; ${test}_target: branch target address.
+; ${test}_has_delay_slot: whether instruction has delay slot.
+; ${test}_cc: condition code numeric value.
+
+ .set r12_value, 0xdead0000
+ .set blink_value, 0xdead0004
+ .set limm_value, 0xdead0008
+ ; Just an integer
+ .set r4_value, 0xdead000c
+ ; Just an integer
+ .set r5_value, 0xdead0010
+ ; offset index for BI [c]
+ .set r7_value, 4
+ .set u6_value, 0x20
+ .set s12_target, 0x100
+
+ mov r12, @r12_value
+ mov r4, @r4_value
+ mov r5, @r5_value
+ mov r7, @r7_value
+ mov blink, @blink_value
+#ifdef TEST_JLI
+ ; jli_base aux regnum = 0x290
+ sr jli_table, [0x290]
+#endif
+
+start_branch_tests:
+
+#ifdef TEST_J
+
+#define TEST_NAME j_c
+ ; j [c]
+ .set j_c_target, @r4_value
+ .set j_c_has_delay_slot, 0
+ .set j_c_cc, 0
+ j_c_start:
+ j [r4]
+ j_c_end:
+
+ ; j [blink]
+ .set j_blink_target, @blink_value
+ .set j_blink_has_delay_slot, 0
+ .set j_blink_cc, 0
+ mov blink, @j_blink_target
+ j_blink_start:
+ j [blink]
+ j_blink_end:
+
+ ; j limm
+ .set j_limm_target, @limm_value
+ .set j_limm_has_delay_slot, 0
+ .set j_limm_cc, 0
+ j_limm_start:
+ j @j_limm_target
+ j_limm_end:
+
+ ; j u6
+ .set j_u6_target, @u6_value
+ .set j_u6_has_delay_slot, 0
+ .set j_u6_cc, 0
+ j_u6_start:
+ j @j_u6_target
+ j_u6_end:
+
+ ; j s12
+ .set j_s12_target, @s12_target
+ .set j_s12_has_delay_slot, 0
+ .set j_s12_cc, 0
+ j_s12_start:
+ j @j_s12_target
+ j_s12_end:
+
+ ; j.d [c]
+ .set j_d_c_target, @r4_value
+ .set j_d_c_has_delay_slot, 1
+ .set j_d_c_cc, 0
+ j_d_c_start:
+ j.d [r4]
+ j_d_c_end:
+ nop_s
+
+ ; j.d [blink]
+ .set j_d_blink_target, @blink_value
+ .set j_d_blink_has_delay_slot, 1
+ .set j_d_blink_cc, 0
+ j_d_blink_start:
+ j.d [blink]
+ j_d_blink_end:
+ nop_s
+
+ ; j.d u6
+ .set j_d_u6_target, @u6_value
+ .set j_d_u6_has_delay_slot, 1
+ .set j_d_u6_cc, 0
+ j_d_u6_start:
+ j.d @j_d_u6_target
+ j_d_u6_end:
+ nop_s
+
+ ; j.d s12
+ .set j_d_s12_target, @s12_target
+ .set j_d_s12_has_delay_slot, 1
+ .set j_d_s12_cc, 0
+ j_d_s12_start:
+ j.d @j_d_s12_target
+ j_d_s12_end:
+ nop_s
+
+ ; j_s [b]
+ .set j_s_b_target, @r12_value
+ .set j_s_b_has_delay_slot, 0
+ .set j_s_b_cc, 0
+ j_s_b_start:
+ j_s [r12]
+ j_s_b_end:
+
+ ; j_s.d [b]
+ .set j_s_d_b_target, @r12_value
+ .set j_s_d_b_has_delay_slot, 1
+ .set j_s_d_b_cc, 0
+ j_s_d_b_start:
+ j_s.d [r12]
+ j_s_d_b_end:
+ nop_s
+
+ ; j_s [blink]
+ .set j_s_blink_target, @blink_value
+ .set j_s_blink_has_delay_slot, 0
+ .set j_s_blink_cc, 0
+ j_s_blink_start:
+ j_s [blink]
+ j_s_blink_end:
+
+ ; j_s.d [blink]
+ .set j_s_d_blink_target, @blink_value
+ .set j_s_d_blink_has_delay_slot, 1
+ .set j_c_cc, 0
+ j_s_d_blink_start:
+ j_s.d [blink]
+ j_s_d_blink_end:
+ nop_s
+#endif /* TEST_J */
+
+#ifdef TEST_JCC
+ ; jcc [c]
+ .set jcc_c_target, @r4_value
+ .set jcc_c_has_delay_slot, 0
+ .set jcc_c_cc, 1
+ jcc_c_start:
+ jeq [r4]
+ jcc_c_end:
+
+ ; jcc [blink]
+ .set jcc_blink_target, @blink_value
+ .set jcc_blink_has_delay_slot, 0
+ .set jcc_blink_cc, 2
+ jcc_blink_start:
+ jnz [blink]
+ jcc_blink_end:
+
+ ; jcc limm
+ .set jcc_limm_target, @limm_value
+ .set jcc_limm_has_delay_slot, 0
+ .set jcc_limm_cc, 9
+ jcc_limm_start:
+ jgt @jcc_limm_target
+ jcc_limm_end:
+
+ ; jcc u6
+ .set jcc_u6_target, @u6_value
+ .set jcc_u6_has_delay_slot, 0
+ .set jcc_u6_cc, 0xA
+ jcc_u6_start:
+ jge @jcc_u6_target
+ jcc_u6_end:
+
+ ; jcc.d [c]
+ .set jcc_d_c_target, @r4_value
+ .set jcc_d_c_has_delay_slot, 1
+ .set jcc_d_c_cc, 0xB
+ jcc_d_c_start:
+ jlt.d [r4]
+ jcc_d_c_end:
+ nop_s
+
+ ; jcc.d [blink]
+ .set jcc_d_blink_target, @blink_value
+ .set jcc_d_blink_has_delay_slot, 1
+ .set jcc_d_blink_cc, 0xC
+ jcc_d_blink_start:
+ jle.d [blink]
+ jcc_d_blink_end:
+ nop_s
+
+ ; jcc.d u6
+ .set jcc_d_u6_target, @u6_value
+ .set jcc_d_u6_has_delay_slot, 1
+ .set jcc_d_u6_cc, 0xE
+ jcc_d_u6_start:
+ jls.d @jcc_d_u6_target
+ jcc_d_u6_end:
+ nop_s
+
+ ; jeq_s [blink]
+ .set jcc_eq_s_blink_target, @blink_value
+ .set jcc_eq_s_blink_has_delay_slot, 0
+ .set jcc_eq_s_blink_cc, 1
+ jcc_eq_s_blink_start:
+ jeq_s [blink]
+ jcc_eq_s_blink_end:
+
+ ; jne_s [blink]
+ .set jcc_ne_s_blink_target, @blink_value
+ .set jcc_ne_s_blink_has_delay_slot, 0
+ .set jcc_ne_s_blink_cc, 2
+ jcc_ne_s_blink_start:
+ jne_s [blink]
+ jcc_ne_s_blink_end:
+#endif /* TEST_JCC */
+
+#ifdef TEST_JL
+ ; jl [c]
+ .set jl_c_target, @r4_value
+ .set jl_c_has_delay_slot, 0
+ .set jl_c_cc, 0
+ jl_c_start:
+ jl [r4]
+ jl_c_end:
+
+ ; jl limm
+ .set jl_limm_target, @limm_value
+ .set jl_limm_has_delay_slot, 0
+ .set jl_limm_cc, 0
+ jl_limm_start:
+ jl @jl_limm_target
+ jl_limm_end:
+
+ ; jl u6
+ .set jl_u6_target, @u6_value
+ .set jl_u6_has_delay_slot, 0
+ .set jl_u6_cc, 0
+ jl_u6_start:
+ jl @jl_u6_target
+ jl_u6_end:
+
+ ; jl s12
+ .set jl_s12_target, @s12_target
+ .set jl_s12_has_delay_slot, 0
+ .set jl_s12_cc, 0
+ jl_s12_start:
+ jl @jl_s12_target
+ jl_s12_end:
+
+ ; jl.d [c]
+ .set jl_d_c_target, @r4_value
+ .set jl_d_c_has_delay_slot, 1
+ .set jl_d_c_cc, 0
+ jl_d_c_start:
+ jl.d [r4]
+ jl_d_c_end:
+ nop_s
+
+ ; jl.d u6
+ .set jl_d_u6_target, @u6_value
+ .set jl_d_u6_has_delay_slot, 1
+ .set jl_d_u6_cc, 0
+ jl_d_u6_start:
+ jl.d @jl_d_u6_target
+ jl_d_u6_end:
+ nop_s
+
+ ; jl.d s12
+ .set jl_d_s12_target, @s12_target
+ .set jl_d_s12_has_delay_slot, 1
+ .set jl_d_s12_cc, 0
+ jl_d_s12_start:
+ jl.d @jl_d_s12_target
+ jl_d_s12_end:
+ nop_s
+
+ ; jl_s [b]
+ .set jl_s_b_target, @r12_value
+ .set jl_s_b_has_delay_slot, 0
+ .set jl_s_b_cc, 0
+ jl_s_b_start:
+ jl_s [r12]
+ jl_s_b_end:
+
+ ; jl_s.d [b]
+ .set jl_s_d_b_target, @r12_value
+ .set jl_s_d_b_has_delay_slot, 1
+ .set jl_s_d_b_cc, 0
+ jl_s_d_b_start:
+ jl_s.d [r12]
+ jl_s_d_b_end:
+ nop_s
+#endif /* TEST_JL */
+
+#ifdef TEST_JLCC
+ ; jlcc [c]
+ .set jlcc_c_target, @r4_value
+ .set jlcc_c_has_delay_slot, 0
+ .set jlcc_c_cc, 1
+ jlcc_c_start:
+ jleq [r4]
+ jlcc_c_end:
+
+ ; jlcc limm
+ .set jlcc_limm_target, @limm_value
+ .set jlcc_limm_has_delay_slot, 0
+ .set jlcc_limm_cc, 0x9
+ jlcc_limm_start:
+ jlgt @jlcc_limm_target
+ jlcc_limm_end:
+
+ ; jlcc u6
+ .set jlcc_u6_target, @u6_value
+ .set jlcc_u6_has_delay_slot, 0
+ .set jlcc_u6_cc, 0xA
+ jlcc_u6_start:
+ jlge @jlcc_u6_target
+ jlcc_u6_end:
+
+ ; jlcc.d [c]
+ .set jlcc_d_c_target, @r4_value
+ .set jlcc_d_c_has_delay_slot, 1
+ .set jlcc_d_c_cc, 0xB
+ jlcc_d_c_start:
+ jllt.d [r4]
+ jlcc_d_c_end:
+ nop_s
+
+ ; jlcc.d u6
+ .set jlcc_d_u6_target, @u6_value
+ .set jlcc_d_u6_has_delay_slot, 1
+ .set jlcc_d_u6_cc, 0xE
+ jlcc_d_u6_start:
+ jlls.d @jlcc_d_u6_target
+ jlcc_d_u6_end:
+ nop_s
+#endif /* TEST_JLCC */
+
+#ifdef TEST_B
+.Lb_target:
+ ; Artifical nop, so that first b will not branch to itself.
+ nop_s
+ ; b s25
+ .set b_s25_target, @.Lb_target
+ .set b_s25_has_delay_slot, 0
+ .set b_s25_cc, 0
+ b_s25_start:
+ b @b_s25_target
+ b_s25_end:
+
+ ; b.d s25
+ .set b_d_s25_target, @.Lb_target
+ .set b_d_s25_has_delay_slot, 1
+ .set b_d_s25_cc, 0
+ b_d_s25_start:
+ b.d @b_d_s25_target
+ b_d_s25_end:
+ nop_s
+
+ ; b_s s10
+ .set b_s_s10_target, @.Lb_target
+ .set b_s_s10_has_delay_slot, 0
+ .set b_s_s10_cc, 0
+ b_s_s10_start:
+ b_s @b_s_s10_target
+ b_s_s10_end:
+#endif /* TEST_B */
+
+#ifdef TEST_BBIT
+
+; Due to specifics of bbit implementation in assembler, only local symbols can
+; be used as a branch targets for bbit and brcc.
+; bbits and brcc don't have condition code set to anything.
+.Lbbit_target:
+ nop_s
+
+ ; bbit0.nt b,c,s9
+ .set bbit0_nt_b_c_s9_target, @.Lbbit_target
+ .set bbit0_nt_b_c_s9_has_delay_slot, 0
+ .set bbit0_nt_b_c_s9_cc, 0
+ bbit0_nt_b_c_s9_start:
+ bbit0.nt r4,r5,@bbit0_nt_b_c_s9_target
+ bbit0_nt_b_c_s9_end:
+
+ ; bbit0.d.nt b,c,s9
+ .set bbit0_d_nt_b_c_s9_target, @.Lbbit_target
+ .set bbit0_d_nt_b_c_s9_has_delay_slot, 1
+ .set bbit0_d_nt_b_c_s9_cc, 0
+ bbit0_d_nt_b_c_s9_start:
+ bbit0.d.nt r4,r5,@.Lbbit_target
+ bbit0_d_nt_b_c_s9_end:
+ nop_s
+
+ ; bbit0.t b,c,s9
+ .set bbit0_t_b_c_s9_target, @.Lbbit_target
+ .set bbit0_t_b_c_s9_has_delay_slot, 0
+ .set bbit0_t_b_c_s9_cc, 0
+ bbit0_t_b_c_s9_start:
+ bbit0.t r4,r5,@.Lbbit_target
+ bbit0_t_b_c_s9_end:
+
+ ; bbit0.d.t b,c,s9
+ .set bbit0_d_t_b_c_s9_target, @.Lbbit_target
+ .set bbit0_d_t_b_c_s9_has_delay_slot, 1
+ .set bbit0_d_t_b_c_s9_cc, 0
+ bbit0_d_t_b_c_s9_start:
+ bbit0.d.t r4,r5,@.Lbbit_target
+ bbit0_d_t_b_c_s9_end:
+ nop_s
+
+ ; bbit0.nt b,u6,s9
+ .set bbit0_nt_b_u6_s9_target, @.Lbbit_target
+ .set bbit0_nt_b_u6_s9_has_delay_slot, 0
+ .set bbit0_nt_b_u6_s9_cc, 0
+ bbit0_nt_b_u6_s9_start:
+ bbit0.nt r4,u6_value,@.Lbbit_target
+ bbit0_nt_b_u6_s9_end:
+
+ ; bbit0.d.nt b,u6,s9
+ .set bbit0_d_nt_b_u6_s9_target, @.Lbbit_target
+ .set bbit0_d_nt_b_u6_s9_has_delay_slot, 1
+ .set bbit0_d_nt_b_u6_s9_cc, 0
+ bbit0_d_nt_b_u6_s9_start:
+ bbit0.d.nt r4,u6_value,@.Lbbit_target
+ bbit0_d_nt_b_u6_s9_end:
+ nop_s
+
+ ; bbit0.nt b,u6,s9
+ .set bbit0_t_b_u6_s9_target, @.Lbbit_target
+ .set bbit0_t_b_u6_s9_has_delay_slot, 0
+ .set bbit0_t_b_u6_s9_cc, 0
+ bbit0_t_b_u6_s9_start:
+ bbit0.t r4,u6_value,@.Lbbit_target
+ bbit0_t_b_u6_s9_end:
+
+ ; bbit0.d.nt b,u6,s9
+ .set bbit0_d_t_b_u6_s9_target, @.Lbbit_target
+ .set bbit0_d_t_b_u6_s9_has_delay_slot, 1
+ .set bbit0_d_t_b_u6_s9_cc, 0
+ bbit0_d_t_b_u6_s9_start:
+ bbit0.d.t r4,u6_value,@.Lbbit_target
+ bbit0_d_t_b_u6_s9_end:
+ nop_s
+
+ ; bbit0.nt b,limm,s9
+ .set bbit0_nt_b_limm_s9_target, @.Lbbit_target
+ .set bbit0_nt_b_limm_s9_has_delay_slot, 0
+ .set bbit0_nt_b_limm_s9_cc, 0
+ bbit0_nt_b_limm_s9_start:
+ bbit0.nt r4,limm_value,@.Lbbit_target
+ bbit0_nt_b_limm_s9_end:
+
+ ; bbit0.t b,limm,s9
+ .set bbit0_t_b_limm_s9_target, @.Lbbit_target
+ .set bbit0_t_b_limm_s9_has_delay_slot, 0
+ .set bbit0_t_b_limm_s9_cc, 0
+ bbit0_t_b_limm_s9_start:
+ bbit0.t r4,limm_value,@.Lbbit_target
+ bbit0_t_b_limm_s9_end:
+
+ ; bbit0.nt limm,c,s9
+ .set bbit0_nt_limm_c_s9_target, @.Lbbit_target
+ .set bbit0_nt_limm_c_s9_has_delay_slot, 0
+ .set bbit0_nt_limm_c_s9_cc, 0
+ bbit0_nt_limm_c_s9_start:
+ bbit0.nt limm_value,r4,@.Lbbit_target
+ bbit0_nt_limm_c_s9_end:
+
+ ; bbit0.t limm,c,s9
+ .set bbit0_t_limm_c_s9_target, @.Lbbit_target
+ .set bbit0_t_limm_c_s9_has_delay_slot, 0
+ .set bbit0_t_limm_c_s9_cc, 0
+ bbit0_t_limm_c_s9_start:
+ bbit0.t limm_value,r4,@.Lbbit_target
+ bbit0_t_limm_c_s9_end:
+
+ ; bbit0.nt limm,u6,s9
+ .set bbit0_nt_limm_u6_s9_target, @.Lbbit_target
+ .set bbit0_nt_limm_u6_s9_has_delay_slot, 0
+ .set bbit0_nt_limm_u6_s9_cc, 0
+ bbit0_nt_limm_u6_s9_start:
+ bbit0.nt limm_value,u6_value,@.Lbbit_target
+ bbit0_nt_limm_u6_s9_end:
+
+ ; bbit0.t limm,u6,s9
+ .set bbit0_t_limm_u6_s9_target, @.Lbbit_target
+ .set bbit0_t_limm_u6_s9_has_delay_slot, 0
+ .set bbit0_t_limm_u6_s9_cc, 0
+ bbit0_t_limm_u6_s9_start:
+ bbit0.t limm_value,u6_value,@.Lbbit_target
+ bbit0_t_limm_u6_s9_end:
+
+ ; bbit1.nt b,c,s9
+ .set bbit1_nt_b_c_s9_target, @.Lbbit_target
+ .set bbit1_nt_b_c_s9_has_delay_slot, 0
+ .set bbit1_nt_b_c_s9_cc, 0
+ bbit1_nt_b_c_s9_start:
+ bbit1.nt r4,r5,@.Lbbit_target
+ bbit1_nt_b_c_s9_end:
+
+ ; bbit1.d.nt b,c,s9
+ .set bbit1_d_nt_b_c_s9_target, @.Lbbit_target
+ .set bbit1_d_nt_b_c_s9_has_delay_slot, 1
+ .set bbit1_d_nt_b_c_s9_cc, 0
+ bbit1_d_nt_b_c_s9_start:
+ bbit1.d.nt r4,r5,@.Lbbit_target
+ bbit1_d_nt_b_c_s9_end:
+ nop_s
+
+ ; bbit1.t b,c,s9
+ .set bbit1_t_b_c_s9_target, @.Lbbit_target
+ .set bbit1_t_b_c_s9_has_delay_slot, 0
+ .set bbit1_t_b_c_s9_cc, 0
+ bbit1_t_b_c_s9_start:
+ bbit1.t r4,r5,@.Lbbit_target
+ bbit1_t_b_c_s9_end:
+
+ ; bbit1.d.t b,c,s9
+ .set bbit1_d_t_b_c_s9_target, @.Lbbit_target
+ .set bbit1_d_t_b_c_s9_has_delay_slot, 1
+ .set bbit1_d_t_b_c_s9_cc, 0
+ bbit1_d_t_b_c_s9_start:
+ bbit1.d.t r4,r5,@.Lbbit_target
+ bbit1_d_t_b_c_s9_end:
+ nop_s
+
+ ; bbit1.nt b,u6,s9
+ .set bbit1_nt_b_u6_s9_target, @.Lbbit_target
+ .set bbit1_nt_b_u6_s9_has_delay_slot, 0
+ .set bbit1_nt_b_u6_s9_cc, 0
+ bbit1_nt_b_u6_s9_start:
+ bbit1.nt r4,u6_value,@.Lbbit_target
+ bbit1_nt_b_u6_s9_end:
+
+ ; bbit1.d.nt b,u6,s9
+ .set bbit1_d_nt_b_u6_s9_target, @.Lbbit_target
+ .set bbit1_d_nt_b_u6_s9_has_delay_slot, 1
+ .set bbit1_d_nt_b_u6_s9_cc, 0
+ bbit1_d_nt_b_u6_s9_start:
+ bbit1.d.nt r4,u6_value,@.Lbbit_target
+ bbit1_d_nt_b_u6_s9_end:
+ nop_s
+
+ ; bbit1.nt b,u6,s9
+ .set bbit1_t_b_u6_s9_target, @.Lbbit_target
+ .set bbit1_t_b_u6_s9_has_delay_slot, 0
+ .set bbit1_t_b_u6_s9_cc, 0
+ bbit1_t_b_u6_s9_start:
+ bbit1.t r4,u6_value,@.Lbbit_target
+ bbit1_t_b_u6_s9_end:
+
+ ; bbit1.d.nt b,u6,s9
+ .set bbit1_d_t_b_u6_s9_target, @.Lbbit_target
+ .set bbit1_d_t_b_u6_s9_has_delay_slot, 1
+ .set bbit1_d_t_b_u6_s9_cc, 0
+ bbit1_d_t_b_u6_s9_start:
+ bbit1.d.t r4,u6_value,@.Lbbit_target
+ bbit1_d_t_b_u6_s9_end:
+ nop_s
+
+ ; bbit1.nt b,limm,s9
+ .set bbit1_nt_b_limm_s9_target, @.Lbbit_target
+ .set bbit1_nt_b_limm_s9_has_delay_slot, 0
+ .set bbit1_nt_b_limm_s9_cc, 0
+ bbit1_nt_b_limm_s9_start:
+ bbit1.nt r4,limm_value,@.Lbbit_target
+ bbit1_nt_b_limm_s9_end:
+
+ ; bbit1.t b,limm,s9
+ .set bbit1_t_b_limm_s9_target, @.Lbbit_target
+ .set bbit1_t_b_limm_s9_has_delay_slot, 0
+ .set bbit1_t_b_limm_s9_cc, 0
+ bbit1_t_b_limm_s9_start:
+ bbit1.t r4,limm_value,@.Lbbit_target
+ bbit1_t_b_limm_s9_end:
+
+ ; bbit1.nt limm,c,s9
+ .set bbit1_nt_limm_c_s9_target, @.Lbbit_target
+ .set bbit1_nt_limm_c_s9_has_delay_slot, 0
+ .set bbit1_nt_limm_c_s9_cc, 0
+ bbit1_nt_limm_c_s9_start:
+ bbit1.nt limm_value,r4,@.Lbbit_target
+ bbit1_nt_limm_c_s9_end:
+
+ ; bbit1.t limm,c,s9
+ .set bbit1_t_limm_c_s9_target, @.Lbbit_target
+ .set bbit1_t_limm_c_s9_has_delay_slot, 0
+ .set bbit1_t_limm_c_s9_cc, 0
+ bbit1_t_limm_c_s9_start:
+ bbit1.t limm_value,r4,@.Lbbit_target
+ bbit1_t_limm_c_s9_end:
+
+ ; bbit1.nt limm,u6,s9
+ .set bbit1_nt_limm_u6_s9_target, @.Lbbit_target
+ .set bbit1_nt_limm_u6_s9_has_delay_slot, 0
+ .set bbit1_nt_limm_u6_s9_cc, 0
+ bbit1_nt_limm_u6_s9_start:
+ bbit1.nt limm_value,u6_value,@.Lbbit_target
+ bbit1_nt_limm_u6_s9_end:
+
+ ; bbit1.t limm,u6,s9
+ .set bbit1_t_limm_u6_s9_target, @.Lbbit_target
+ .set bbit1_t_limm_u6_s9_has_delay_slot, 0
+ .set bbit1_t_limm_u6_s9_cc, 0
+ bbit1_t_limm_u6_s9_start:
+ bbit1.t limm_value,u6_value,@.Lbbit_target
+ bbit1_t_limm_u6_s9_end:
+#endif /* TEST_BBIT */
+
+#ifdef TEST_BCC
+.Lbcc_target:
+ ; bcc s21
+ .set bcc_s21_target, @.Lbcc_target
+ .set bcc_s21_has_delay_slot, 0
+ .set bcc_s21_cc, 1
+ bcc_s21_start:
+ ; beq @bcc_s21_target
+ beq @.Lbcc_target
+ bcc_s21_end:
+
+ ; bcc.d s21
+ .set bcc_d_s21_target, @.Lbcc_target
+ .set bcc_d_s21_has_delay_slot, 1
+ .set bcc_d_s21_cc, 1
+ bcc_d_s21_start:
+ beq.d @bcc_d_s21_target
+ bcc_d_s21_end:
+ nop_s
+
+.Lbcc_s_target:
+ ; beq_s s10
+ .set beq_s_s10_target, @.Lbcc_s_target
+ .set beq_s_s10_has_delay_slot, 0
+ .set beq_s_s10_cc, 1
+ beq_s_s10_start:
+ # beq_s.d @beq_s_s10_target
+ beq_s @.Lbcc_s_target
+ beq_s_s10_end:
+
+ ; bne_s s10
+ .set bne_s_s10_target, @.Lbcc_s_target
+ .set bne_s_s10_has_delay_slot, 0
+ .set bne_s_s10_cc, 2
+ bne_s_s10_start:
+ bne_s @bne_s_s10_target
+ bne_s_s10_end:
+
+ ; bgt_s s7
+ .set bgt_s_s7_target, @.Lbcc_s_target
+ .set bgt_s_s7_has_delay_slot, 0
+ .set bgt_s_s7_cc, 0x9
+ bgt_s_s7_start:
+ bgt_s @bgt_s_s7_target
+ bgt_s_s7_end:
+
+ ; bge_s s7
+ .set bge_s_s7_target, @.Lbcc_s_target
+ .set bge_s_s7_has_delay_slot, 0
+ .set bge_s_s7_cc, 0xA
+ bge_s_s7_start:
+ bge_s @bge_s_s7_target
+ bge_s_s7_end:
+
+ ; blt_s s7
+ .set blt_s_s7_target, @.Lbcc_s_target
+ .set blt_s_s7_has_delay_slot, 0
+ .set blt_s_s7_cc, 0xB
+ blt_s_s7_start:
+ blt_s @blt_s_s7_target
+ blt_s_s7_end:
+
+ ; ble_s s7
+ .set ble_s_s7_target, @.Lbcc_s_target
+ .set ble_s_s7_has_delay_slot, 0
+ .set ble_s_s7_cc, 0xC
+ ble_s_s7_start:
+ ble_s @ble_s_s7_target
+ ble_s_s7_end:
+
+ ; bhi_s s7
+ .set bhi_s_s7_target, @.Lbcc_s_target
+ .set bhi_s_s7_has_delay_slot, 0
+ .set bhi_s_s7_cc, 0xD
+ bhi_s_s7_start:
+ bhi_s @bhi_s_s7_target
+ bhi_s_s7_end:
+
+ ; bhs_s s7
+ .set bhs_s_s7_target, @.Lbcc_s_target
+ .set bhs_s_s7_has_delay_slot, 0
+ .set bhs_s_s7_cc, 0x6
+ bhs_s_s7_start:
+ bhs_s @bhs_s_s7_target
+ bhs_s_s7_end:
+
+ ; blo_s s7
+ .set blo_s_s7_target, @.Lbcc_s_target
+ .set blo_s_s7_has_delay_slot, 0
+ .set blo_s_s7_cc, 0x5
+ blo_s_s7_start:
+ blo_s @blo_s_s7_target
+ blo_s_s7_end:
+
+ ; bls_s s7
+ .set bls_s_s7_target, @.Lbcc_s_target
+ .set bls_s_s7_has_delay_slot, 0
+ .set bls_s_s7_cc, 0xE
+ bls_s_s7_start:
+ bls_s @bls_s_s7_target
+ bls_s_s7_end:
+#endif /* TEST_BCC */
+
+#ifdef TEST_BI
+ ; bi [c]
+ .set bi_c_target, @bi_c_end + (@r7_value << 2)
+ .set bi_c_has_delay_slot, 0
+ .set bi_c_cc, 0
+ bi_c_start:
+ bi [r7]
+ bi_c_end:
+
+ ; bih [c]
+ .set bih_c_target, @bih_c_end + (@r7_value << 1)
+ .set bih_c_has_delay_slot, 0
+ .set bih_c_cc, 0
+ bih_c_start:
+ bih [r7]
+ bih_c_end:
+#endif /* TEST_BI */
+
+#ifdef TEST_BL
+.Lbl_target:
+ ; bl s25
+ .set bl_s25_target, @.Lbl_target
+ .set bl_s25_has_delay_slot, 0
+ .set bl_s25_cc, 0
+ bl_s25_start:
+ bl @bl_s25_target
+ bl_s25_end:
+
+ ; bl.d s25
+ .set bl_d_s25_target, @.Lbl_target
+ .set bl_d_s25_has_delay_slot, 1
+ .set bl_d_s25_cc, 0
+ bl_d_s25_start:
+ bl.d @bl_d_s25_target
+ bl_d_s25_end:
+ nop_s
+
+ ; bl_s s13
+ .set bl_s_s13_target, @.Lbl_target
+ .set bl_s_s13_has_delay_slot, 0
+ .set bl_s_s13_cc, 0
+ bl_s_s13_start:
+ bl_s @bl_s_s13_target
+ bl_s_s13_end:
+
+ ; blcc s21
+ .set blcc_s21_target, @.Lbl_target
+ .set blcc_s21_has_delay_slot, 0
+ .set blcc_s21_cc, 1
+ blcc_s21_start:
+ bleq @blcc_s21_target
+ blcc_s21_end:
+
+ ; blcc.d s21
+ .set blcc_d_s21_target, @.Lbl_target
+ .set blcc_d_s21_has_delay_slot, 1
+ .set blcc_d_s21_cc, 2
+ blcc_d_s21_start:
+ blnz.d @blcc_d_s21_target
+ blcc_d_s21_end:
+ nop_s
+#endif /* TEST_BL */
+
+#ifdef TEST_BRCC
+.Lbrcc_target:
+ ; breq.nt b,c,s9
+ .set breq_nt_b_c_s9_target, @.Lbrcc_target
+ .set breq_nt_b_c_s9_has_delay_slot, 0
+ .set breq_nt_b_c_s9_cc, 1
+ breq_nt_b_c_s9_start:
+ breq.nt r4,r5,@.Lbrcc_target
+ breq_nt_b_c_s9_end:
+
+ ; breq.d.nt b,c,s9
+ .set breq_d_nt_b_c_s9_target, @.Lbrcc_target
+ .set breq_d_nt_b_c_s9_has_delay_slot, 1
+ .set breq_d_nt_b_c_s9_cc, 1
+ breq_d_nt_b_c_s9_start:
+ breq.d.nt r4,r5,@.Lbrcc_target
+ breq_d_nt_b_c_s9_end:
+ nop_s
+
+ ; breq.t b,c,s9
+ .set breq_t_b_c_s9_target, @.Lbrcc_target
+ .set breq_t_b_c_s9_has_delay_slot, 0
+ .set breq_t_b_c_s9_cc, 1
+ breq_t_b_c_s9_start:
+ breq.t r4,r5,@.Lbrcc_target
+ breq_t_b_c_s9_end:
+
+ ; breq.d.t b,c,s9
+ .set breq_d_t_b_c_s9_target, @.Lbrcc_target
+ .set breq_d_t_b_c_s9_has_delay_slot, 1
+ .set breq_d_t_b_c_s9_cc, 1
+ breq_d_t_b_c_s9_start:
+ breq.d.t r4,r5,@.Lbrcc_target
+ breq_d_t_b_c_s9_end:
+ nop_s
+
+ ; breq.nt b,u6,s9
+ .set breq_nt_b_u6_s9_target, @.Lbrcc_target
+ .set breq_nt_b_u6_s9_has_delay_slot, 0
+ .set breq_nt_b_u6_s9_cc, 1
+ breq_nt_b_u6_s9_start:
+ breq.nt r4,u6_value,@.Lbrcc_target
+ breq_nt_b_u6_s9_end:
+
+ ; breq.d.nt b,u6,s9
+ .set breq_d_nt_b_u6_s9_target, @.Lbrcc_target
+ .set breq_d_nt_b_u6_s9_has_delay_slot, 1
+ .set breq_d_nt_b_u6_s9_cc, 1
+ breq_d_nt_b_u6_s9_start:
+ breq.d.nt r4,u6_value,@.Lbrcc_target
+ breq_d_nt_b_u6_s9_end:
+ nop_s
+
+ ; breq.nt b,u6,s9
+ .set breq_t_b_u6_s9_target, @.Lbrcc_target
+ .set breq_t_b_u6_s9_has_delay_slot, 0
+ .set breq_t_b_u6_s9_cc, 1
+ breq_t_b_u6_s9_start:
+ breq.t r4,u6_value,@.Lbrcc_target
+ breq_t_b_u6_s9_end:
+
+ ; breq.d.nt b,u6,s9
+ .set breq_d_t_b_u6_s9_target, @.Lbrcc_target
+ .set breq_d_t_b_u6_s9_has_delay_slot, 1
+ .set breq_d_t_b_u6_s9_cc, 1
+ breq_d_t_b_u6_s9_start:
+ breq.d.t r4,u6_value,@.Lbrcc_target
+ breq_d_t_b_u6_s9_end:
+ nop_s
+
+ ; breq.nt b,limm,s9
+ .set breq_nt_b_limm_s9_target, @.Lbrcc_target
+ .set breq_nt_b_limm_s9_has_delay_slot, 0
+ .set breq_nt_b_limm_s9_cc, 1
+ breq_nt_b_limm_s9_start:
+ breq.nt r4,limm_value,@.Lbrcc_target
+ breq_nt_b_limm_s9_end:
+
+ ; breq.t b,limm,s9
+ .set breq_t_b_limm_s9_target, @.Lbrcc_target
+ .set breq_t_b_limm_s9_has_delay_slot, 0
+ .set breq_t_b_limm_s9_cc, 1
+ breq_t_b_limm_s9_start:
+ breq.t r4,limm_value,@.Lbrcc_target
+ breq_t_b_limm_s9_end:
+
+ ; breq.nt limm,c,s9
+ .set breq_nt_limm_c_s9_target, @.Lbrcc_target
+ .set breq_nt_limm_c_s9_has_delay_slot, 0
+ .set breq_nt_limm_c_s9_cc, 1
+ breq_nt_limm_c_s9_start:
+ breq.nt limm_value,r4,@.Lbrcc_target
+ breq_nt_limm_c_s9_end:
+
+ ; breq.t limm,c,s9
+ .set breq_t_limm_c_s9_target, @.Lbrcc_target
+ .set breq_t_limm_c_s9_has_delay_slot, 0
+ .set breq_t_limm_c_s9_cc, 1
+ breq_t_limm_c_s9_start:
+ breq.t limm_value,r4,@.Lbrcc_target
+ breq_t_limm_c_s9_end:
+
+ ; breq.nt limm,u6,s9
+ .set breq_nt_limm_u6_s9_target, @.Lbrcc_target
+ .set breq_nt_limm_u6_s9_has_delay_slot, 0
+ .set breq_nt_limm_u6_s9_cc, 1
+ breq_nt_limm_u6_s9_start:
+ breq.nt limm_value,u6_value,@.Lbrcc_target
+ breq_nt_limm_u6_s9_end:
+
+ ; breq.t limm,u6,s9
+ .set breq_t_limm_u6_s9_target, @.Lbrcc_target
+ .set breq_t_limm_u6_s9_has_delay_slot, 0
+ .set breq_t_limm_u6_s9_cc, 1
+ breq_t_limm_u6_s9_start:
+ breq.t limm_value,u6_value,@.Lbrcc_target
+ breq_t_limm_u6_s9_end:
+
+ ; brne_s b,0,s8
+ .set brne_s_b_0_s8_target, @.Lbrcc_target
+ .set brne_s_b_0_s8_has_delay_slot, 0
+ .set brne_s_b_0_s8_cc, 1
+ brne_s_b_0_s8_start:
+ brne r12,0,@.Lbrcc_target
+ brne_s_b_0_s8_end:
+
+ ; breq_s b,0,s8
+ .set breq_s_b_0_s8_target, @.Lbrcc_target
+ .set breq_s_b_0_s8_has_delay_slot, 0
+ .set breq_s_b_0_s8_cc, 1
+ breq_s_b_0_s8_start:
+ breq r12,0,@.Lbrcc_target
+ breq_s_b_0_s8_end:
+#endif /* TEST_BRCC */
+
+#ifdef TEST_JLI
+ ; jli_s u10
+ .set jli_s_u10_target, @jli_target
+ .set jli_s_u10_has_delay_slot, 0
+ .set jli_s_u10_cc, 0
+ jli_s_u10_start:
+ jli_s jli_offset
+ jli_s_u10_end:
+#endif
+
+#ifdef TEST_LEAVE_S
+ ; leave_s
+ .set leave_s_target, @blink_value
+ .set leave_s_has_delay_slot, 0
+ .set leave_s_cc, 0
+ leave_s_start:
+ ; leave_s [r13-gp,fp,blink,pcl]
+ leave_s (14 + 16 + 32 + 64)
+ leave_s_end:
+#endif
+
+#ifdef TEST_LPCC
+ ; lpcc
+ .set lpcc_u7_target, @.Llpcc_end
+ .set lpcc_u7_has_delay_slot, 0
+ .set lpcc_u7_cc, 1
+ lpcc_u7_start:
+ lpeq @lpcc_u7_target
+ lpcc_u7_end:
+ nop
+ nop
+.Llpcc_end:
+#endif
+
+.Lend:
+
diff --git a/gdb/testsuite/gdb.arch/arc-decode-insn.exp b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
new file mode 100644
index 0000000..329b4ef
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-decode-insn.exp
@@ -0,0 +1,136 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 Synopsys 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/>.
+
+
+# This tests provides certain degree of testing for arch/arc-insn.c functions,
+# however it is not a comprehensive testsuite that would go through all
+# possible ARC instructions - instead this particular test is focused on branch
+# instructions and whether branch targets are evaluated properly. Most of the
+# non-branch aspects of instruction decoder are used during prologue analysis,
+# so are indirictly tested there.
+
+# To maintain separation of test data and test logic, all of the information
+# about instructions, like if it has delay slot, condition code, branch target
+# address, is all specified in the test assembly file as a symbols, while this
+# test case reads those symbols to learn which values are right, then compares
+# values coming from arc-insn.c with those found in symbols. More information
+# about requirements to actual test cases can be found in corresponding
+# assembly file of this test case (arc-decode-insn.S).
+
+if {![istarget "arc*-*-*"]} then {
+ verbose "Skipping ARC decoder test."
+ return
+}
+
+standard_testfile .S
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "" ] != "" } {
+ untested arc-decode-insn.exp
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] {
+ fail "Can't run to main"
+ return 0
+}
+
+# Helper function that reads properties of instruction from the ELF file via
+# its symbols and then confirms that decoder output aligns to the expected
+# values.
+proc test_branch_insn { test_name } {
+
+ # Make messages for failed cases more clear, by using hex in them.
+ set pc [get_hexadecimal_valueof &${test_name}_start -1]
+
+ # Calculate instruction length, based on ${test_name}_end symbol.
+ set end_pc [get_hexadecimal_valueof &${test_name}_end -1]
+ set length [expr $end_pc - $pc]
+
+ set target_address [get_hexadecimal_valueof &${test_name}_target -1]
+
+ # Figure out if there is a delay slot, using symbol
+ # ${test_name}_has_delay_slot. Note that it should be read via &,
+ # otherwise it would try to print value at the address specified in
+ # ${test_name}_has_delay_slot, while a symbol value itself is required.
+ if { 0 == [get_integer_valueof &${test_name}_has_delay_slot 0] } {
+ set has_delay_slot 0
+ } else {
+ set has_delay_slot 1
+ }
+
+ set cc [get_hexadecimal_valueof &${test_name}_cc 0]
+
+ gdb_test_sequence "mt print arc arc-instruction $pc" "" "
+ length_with_limm=$length
+ cc=$cc
+ is_control_flow=1
+ has_delay_slot=$has_delay_slot
+ branch_target=$target_address"
+}
+
+set branch_test_list { }
+
+# Add items in the same groups as they can be enabled/disabled in assembly
+# file.
+lappend branch_test_list \
+ j_c j_blink j_limm j_u6 j_s12 j_d_c j_d_blink j_d_u6
+lappend branch_test_list \
+ jcc_c jcc_blink jcc_limm jcc_u6 jcc_d_c jcc_d_blink jcc_d_u6 jcc_eq_s_blink \
+ jcc_ne_s_blink
+lappend branch_test_list \
+ jl_c jl_limm jl_u6 jl_s12 jl_d_c jl_d_u6 jl_d_s12 jl_s_b jl_s_d_b
+lappend branch_test_list \
+ jlcc_c jlcc_limm jlcc_u6 jlcc_d_c jlcc_d_u6
+lappend branch_test_list \
+ b_s25 b_d_s25 b_s_s10
+lappend branch_test_list \
+ bbit0_nt_b_c_s9 bbit0_d_nt_b_c_s9 bbit0_t_b_c_s9 bbit0_d_t_b_c_s9 \
+ bbit0_nt_b_u6_s9 bbit0_d_nt_b_u6_s9 bbit0_t_b_u6_s9 bbit0_d_t_b_u6_s9 \
+ bbit0_nt_b_limm_s9 bbit0_t_b_limm_s9 bbit0_nt_limm_c_s9 bbit0_t_limm_c_s9 \
+ bbit0_nt_limm_u6_s9 bbit0_t_limm_u6_s9 \
+ bbit1_nt_b_c_s9 bbit1_d_nt_b_c_s9 bbit1_t_b_c_s9 bbit1_d_t_b_c_s9 \
+ bbit1_nt_b_u6_s9 bbit1_d_nt_b_u6_s9 bbit1_t_b_u6_s9 bbit1_d_t_b_u6_s9 \
+ bbit1_nt_b_limm_s9 bbit1_t_b_limm_s9 bbit1_nt_limm_c_s9 bbit1_t_limm_c_s9 \
+ bbit1_nt_limm_u6_s9 bbit1_t_limm_u6_s9
+lappend branch_test_list \
+ bcc_s21 bcc_d_s21 \
+ beq_s_s10 bne_s_s10 bgt_s_s7 bge_s_s7 blt_s_s7 ble_s_s7 bhi_s_s7 bhs_s_s7 \
+ blo_s_s7 bls_s_s7
+lappend branch_test_list \
+ bi_c bih_c
+lappend branch_test_list \
+ bl_s25 bl_d_s25 bl_s_s13 \
+ blcc_s21 blcc_d_s21
+lappend branch_test_list \
+ breq_nt_b_c_s9 breq_d_nt_b_c_s9 breq_t_b_c_s9 breq_d_t_b_c_s9 \
+ breq_nt_b_u6_s9 breq_d_nt_b_u6_s9 breq_t_b_u6_s9 breq_d_t_b_u6_s9 \
+ breq_nt_b_limm_s9 breq_t_b_limm_s9 breq_nt_limm_c_s9 breq_t_limm_c_s9 \
+ breq_nt_limm_u6_s9 breq_t_limm_u6_s9
+# lappend branch_test_list jli_s_u10
+lappend branch_test_list leave_s
+lappend branch_test_list lpcc_u7
+
+runto start_branch_tests
+foreach test $branch_test_list {
+ test_branch_insn $test
+}
+
--
2.8.3