arc: Add disassembler helper
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_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 <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.
This commit is contained in:
parent
3be78afded
commit
eea787570f
@ -1,3 +1,23 @@
|
||||
2017-03-28 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_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".
|
||||
|
||||
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
|
||||
|
||||
* arc-tdep (maintenance_print_arc_list): New variable.
|
||||
|
3
gdb/NEWS
3
gdb/NEWS
@ -101,6 +101,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.
|
||||
|
315
gdb/arc-tdep.c
315
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 *);
|
||||
@ -1320,6 +1608,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;
|
||||
|
||||
@ -1340,6 +1648,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,
|
||||
|
@ -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 */
|
||||
|
@ -1,3 +1,7 @@
|
||||
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
|
||||
|
||||
* gdb.texinfo (Synopsys ARC): Add "maint print arc arc-instruction".
|
||||
|
||||
2017-03-22 Yao Qi <yao.qi@linaro.org>
|
||||
|
||||
* python.texi (Inferiors In Python): Remove @code from Python.
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,8 @@
|
||||
2017-03-28 Anton Kolesov <anton.kolesov@synopsys.com>
|
||||
|
||||
* gdb.arch/arc-decode-insn.S: New file.
|
||||
* gdb.arch/arc-decode-insn.exp: Likewise.
|
||||
|
||||
2017-03-21 Ivo Raisr <ivo.raisr@oracle.com>
|
||||
|
||||
PR tdep/20928
|
||||
|
1002
gdb/testsuite/gdb.arch/arc-decode-insn.S
Normal file
1002
gdb/testsuite/gdb.arch/arc-decode-insn.S
Normal file
File diff suppressed because it is too large
Load Diff
132
gdb/testsuite/gdb.arch/arc-decode-insn.exp
Normal file
132
gdb/testsuite/gdb.arch/arc-decode-insn.exp
Normal file
@ -0,0 +1,132 @@
|
||||
# This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
# Copyright 2017 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# 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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user