From patchwork Wed Mar 15 15:17:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Kolesov X-Patchwork-Id: 19587 Received: (qmail 108390 invoked by alias); 15 Mar 2017 15:17:52 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 108368 invoked by uid 89); 15 Mar 2017 15:17:51 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-23.4 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=pc, jd, AUX, lend X-HELO: smtprelay.synopsys.com Received: from smtprelay.synopsys.com (HELO smtprelay.synopsys.com) (198.182.47.9) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 15 Mar 2017 15:17:45 +0000 Received: from mailhost.synopsys.com (mailhost2.synopsys.com [10.13.184.66]) by smtprelay.synopsys.com (Postfix) with ESMTP id 488AF24E05E7 for ; Wed, 15 Mar 2017 08:17:45 -0700 (PDT) Received: from mailhost.synopsys.com (localhost [127.0.0.1]) by mailhost.synopsys.com (Postfix) with ESMTP id 24C8B9D4; Wed, 15 Mar 2017 08:17:45 -0700 (PDT) Received: from akolesov-lab.internal.synopsys.com (akolesov-lab.internal.synopsys.com [10.121.8.134]) by mailhost.synopsys.com (Postfix) with ESMTP id BDFC99B6; Wed, 15 Mar 2017 08:17:43 -0700 (PDT) From: Anton Kolesov To: gdb-patches@sourceware.org Cc: Anton Kolesov , Francois Bedard Subject: [PATCH 4/5 v2] arc: Add disassembler helper Date: Wed, 15 Mar 2017 18:17:38 +0300 Message-Id: <20170315151738.28651-1-Anton.Kolesov@synopsys.com> In-Reply-To: Changes in v2: * Add NEWS entry for "maint print arc arc-instruction" command. * Fix grammar in the comment. * Add arc_disassemble_info function, which was used in this patch, but was declared and defined only in the next patch in a sequence. * Merge arch/arc-insn.{c,h} files into arc-tdep.{c,h} * Improve formatting of output of "maint print arc arc-instruction" command: add whitespaces around the "=" symbol. * Use "prepare_for" in test cases. * Reformat test sequence as a list of string. --- 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 * configure.tgt: Add arc-insn.o. * arc-tdep.c (arc_delayed_print_insn): Make non-static. (dump_arc_instruction_command): New function. (arc_fprintf_disasm): Likewise. (arc_disassemble_info): Likewise. (arc_insn_get_operand_value): Likewise. (arc_insn_get_operand_value_signed): Likewise. (arc_insn_get_memory_base_reg): Likewise. (arc_insn_get_memory_offset): Likewise. (arc_insn_get_branch_target): Likewise. (arc_insn_dump): Likewise. (arc_insn_get_linear_next_pc): Likewise. * arc-tdep.h (arc_delayed_print_insn): Add function declaration. (arc_disassemble_info): Likewise. (arc_insn_get_branch_target): Likewise. (arc_insn_get_linear_next_pc): Likewise. * NEWS: Mention new "maint print arc arc-instruction". gdb/doc/ChangeLog: yyyy-mm-dd Anton Kolesov * gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction". gdb/testsuite/ChangeLog: yyyy-mm-dd Anton Kolesov * gdb.arch/arc-decode-insn.S: New file. * gdb.arch/arc-decode-insn.exp: Likewise. --- gdb/NEWS | 3 + gdb/arc-tdep.c | 315 ++++++++- gdb/arc-tdep.h | 25 + gdb/doc/gdb.texinfo | 4 + gdb/testsuite/gdb.arch/arc-decode-insn.S | 1002 ++++++++++++++++++++++++++++ gdb/testsuite/gdb.arch/arc-decode-insn.exp | 132 ++++ 6 files changed, 1480 insertions(+), 1 deletion(-) 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/NEWS b/gdb/NEWS index cf58595..754a585 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -94,6 +94,9 @@ show disassembler-options The default value is the empty string. Currently, the only supported targets are ARM, PowerPC and S/390. +maint print arc arc-instruction address + Print internal disassembler information about instruction at a given address. + *** Changes in GDB 7.12 * GDB and GDBserver now build with a C++ compiler by default. diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index 5495f2e..639f7dd 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -32,6 +32,7 @@ /* ARC header files. */ #include "opcode/arc.h" +#include "opcodes/arc-dis.h" #include "arc-tdep.h" /* Standard headers. */ @@ -116,6 +117,269 @@ static const char *const core_arcompact_register_names[] = { "lp_count", "reserved", "limm", "pcl", }; +/* Returns an unsigned value of OPERAND_NUM in instruction INSN. + For relative branch instructions returned value is an offset, not an actual + branch target. */ + +static 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; + } +} + +/* Like arc_insn_get_operand_value, but returns a signed value. */ + +static 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; + } +} + +/* Get register with base address of memory operation. */ + +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; +} + +/* Get offset of a memory operation INSN. */ + +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; +} + +/* Functions are sorted in the order as they are used in the + _initialize_arc_tdep (), which uses the same order as gdbarch.h. Static + functions are defined before the first invocation. */ + +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."); +} + +/* Dump INSN into gdb_stdlog. */ + +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)); +} + /* Implement the "write_pc" gdbarch method. In ARC PC register is a normal register so in most cases setting PC value @@ -649,6 +913,30 @@ arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache) return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM); } +/* Copy of gdb_buffered_insn_length_fprintf from disasm.c. */ + +static int ATTRIBUTE_PRINTF (2, 3) +arc_fprintf_disasm (void *stream, const char *format, ...) +{ + return 0; +} + +struct disassemble_info +arc_disassemble_info (struct gdbarch *gdbarch) +{ + struct disassemble_info di; + init_disassemble_info (&di, &null_stream, arc_fprintf_disasm); + di.arch = gdbarch_bfd_arch_info (gdbarch)->arch; + di.mach = gdbarch_bfd_arch_info (gdbarch)->mach; + di.endian = gdbarch_byte_order (gdbarch); + di.read_memory_func = [](bfd_vma memaddr, gdb_byte *myaddr, + unsigned int len, struct disassemble_info *info) + { + return target_read_code (memaddr, myaddr, len); + }; + return di; +} + /* Implement the "skip_prologue" gdbarch method. Skip the prologue for the function at PC. This is done by checking from @@ -701,7 +989,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 +1618,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 +1658,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..1bf1817 100644 --- a/gdb/arc-tdep.h +++ b/gdb/arc-tdep.h @@ -123,4 +123,29 @@ 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); + +/* Return properly initialized disassemble_info for ARC disassembler - it will + not print disassembled instructions to stderr. */ + +struct disassemble_info arc_disassemble_info (struct gdbarch *gdbarch); + +/* 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); + +/* 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); + #endif /* ARC_TDEP_H */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 5cf0f97..344778b 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22105,6 +22105,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 . + +.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..516be28 --- /dev/null +++ b/gdb/testsuite/gdb.arch/arc-decode-insn.exp @@ -0,0 +1,132 @@ +# 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 . + + +# These tests provides certain degree of testing for arc_insn 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 decoder 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 { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return -1 +} + +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] + + # Can't use {} to create a list of items, because variables will not be + # evaluated inside the {}. + gdb_test_sequence "mt print arc arc-instruction $pc" "" [list \ + "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 +} +