Skip to content

Commit

Permalink
RISC-V: Better support for long instructions
Browse files Browse the repository at this point in the history
Commit bb99669 ("RISC-V/gas: allow generating up to 176-bit
instructions with .insn") tried to start supporting long instructions but
it was insufficient.

1.  It heavily depended on the bignum internals (radix of 2^16),
2.  It generates "value conflicts with instruction length" even if a big
    number instruction encoding does not exceed its expected length,
3.  Because long opcode was handled separately (from struct riscv_cl_insn),
    some information like DWARF line number correspondence was missing and
4.  On the disassembler, disassembler dump was limited up to 64-bit.
    For long (unknown) instructions, instruction bits are incorrectly
    zeroed out.

To solve these problems, this commit:

1.  Handles bignum (and its encodings) precisely,
2.  Incorporates long opcode handling into regular struct
    riscv_cl_insn-handling functions.
3.  Adds packet argument to support dumping instructions
    longer than 64-bits.

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_cl_insn): Add long opcode field.
	(create_insn) Clear long opcode marker.
	(install_insn) Install longer opcode as well.
	(s_riscv_insn) Likewise.
	(riscv_ip_hardcode): Make big number handling stricter. Length and
	the value conflicts only if the bignum size exceeds the expected
	length.
	* testsuite/gas/riscv/insn.s: Add testcases such that big number
	handling is required.
	* testsuite/gas/riscv/insn.d: Likewise.
	* testsuite/gas/riscv/insn-na.d: Likewise.
	* testsuite/gas/riscv/insn-dwarf.d: Likewise.

opcodes/ChangeLog:

	* riscv-dis.c (riscv_disassemble_insn): Print unknown instruction
	using the new argument packet.
	(riscv_disassemble_data): Add unused argument packet.
	(print_insn_riscv): Pass packet to the disassemble function.
  • Loading branch information
a4lg committed Nov 19, 2022
1 parent 2e469b6 commit ed49fad
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 14 deletions.
50 changes: 42 additions & 8 deletions gas/config/tc-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ struct riscv_cl_insn
/* The encoded instruction bits. */
insn_t insn_opcode;

/* The long encoded instruction bits ([0] is non-zero if used). */
char insn_long_opcode[RISCV_MAX_INSN_LEN];

/* The frag that contains the instruction. */
struct frag *frag;

Expand Down Expand Up @@ -714,6 +717,7 @@ create_insn (struct riscv_cl_insn *insn, const struct riscv_opcode *mo)
{
insn->insn_mo = mo;
insn->insn_opcode = mo->match;
insn->insn_long_opcode[0] = 0; /* Long insn has non-zero value. */
insn->frag = NULL;
insn->where = 0;
insn->fixp = NULL;
Expand All @@ -725,7 +729,10 @@ static void
install_insn (const struct riscv_cl_insn *insn)
{
char *f = insn->frag->fr_literal + insn->where;
number_to_chars_littleendian (f, insn->insn_opcode, insn_length (insn));
if (insn->insn_long_opcode[0] != 0)
memcpy (f, insn->insn_long_opcode, insn_length (insn));
else
number_to_chars_littleendian (f, insn->insn_opcode, insn_length (insn));
}

/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
Expand Down Expand Up @@ -3481,7 +3488,21 @@ riscv_ip_hardcode (char *str,
values[num++] = (insn_t) imm_expr->X_add_number;
break;
case O_big:
values[num++] = generic_bignum[0];
/* Extract lower 16-bits of a big number. */
if (CHARS_PER_LITTLENUM == 1)
{
offsetT n = imm_expr->X_add_number;
n = n > 2 ? 2 : n;
values[num] = 0;
for (offsetT i = 0; i < n; ++i)
{
values[num] *= (insn_t) LITTLENUM_RADIX;
values[num] |= (insn_t) generic_bignum[n - i - 1];
}
num++;
}
else
values[num++] = (insn_t) generic_bignum[0];
break;
default:
/* The first value isn't constant, so it should be
Expand All @@ -3508,12 +3529,25 @@ riscv_ip_hardcode (char *str,

if (imm_expr->X_op == O_big)
{
if (bytes != imm_expr->X_add_number * CHARS_PER_LITTLENUM)
unsigned int llen = 0;
for (LITTLENUM_TYPE lval = generic_bignum[imm_expr->X_add_number - 1];
lval != 0; llen++)
lval >>= BITS_PER_CHAR;
unsigned int repr_bytes
= (imm_expr->X_add_number - 1) * CHARS_PER_LITTLENUM + llen;
if (bytes < repr_bytes)
return _("value conflicts with instruction length");
char *f = frag_more (bytes);
for (num = 0; num < imm_expr->X_add_number; ++num)
number_to_chars_littleendian (f + num * CHARS_PER_LITTLENUM,
generic_bignum[num], CHARS_PER_LITTLENUM);
for (num = 0; num < imm_expr->X_add_number - 1; ++num)
number_to_chars_littleendian (
ip->insn_long_opcode + num * CHARS_PER_LITTLENUM,
generic_bignum[num],
CHARS_PER_LITTLENUM);
if (llen != 0)
number_to_chars_littleendian (
ip->insn_long_opcode + num * CHARS_PER_LITTLENUM,
generic_bignum[num],
llen);
memset(ip->insn_long_opcode + repr_bytes, 0, bytes - repr_bytes);
return NULL;
}

Expand Down Expand Up @@ -4590,7 +4624,7 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED)
else
as_bad ("%s `%s'", error.msg, error.statement);
}
else if (imm_expr.X_op != O_big)
else
{
gas_assert (insn.insn_mo->pinfo != INSN_MACRO);
append_insn (&insn, &imm_expr, imm_reloc);
Expand Down
10 changes: 9 additions & 1 deletion gas/testsuite/gas/riscv/insn-dwarf.d
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,13 @@ insn.s +69 +0xf6.*
insn.s +70 +0xfe.*
insn.s +71 +0x108.*
insn.s +72 +0x114.*
insn.s +- +0x12a
insn.s +74 +0x12a.*
insn.s +75 +0x134.*
insn.s +76 +0x13e.*
insn.s +77 +0x154.*
insn.s +78 +0x16a.*
insn.s +79 +0x180.*
insn.s +80 +0x196.*
insn.s +81 +0x1ac.*
insn.s +- +0x1c2
#pass
8 changes: 8 additions & 0 deletions gas/testsuite/gas/riscv/insn-na.d
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,11 @@ Disassembly of section .text:
[^:]+:[ ]+007f 0000 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+0000107f 00000000 00000000[ ]+\.byte[ ]+0x7f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+607f 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+007f 0000 0000 0000 8000[ ]+\.byte[ ]+0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
[^:]+:[ ]+007f 0000 0000 0000 8000[ ]+\.byte[ ]+0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
[^:]+:[ ]+607f 89ab 4567 0123 3210 7654 ba98 fedc 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x60, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+607f 89ab 4567 0123 3210 7654 ba98 fedc 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x60, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+607f 33cc 55aa cdef 89ab 4567 0123 3210 7654 ba98 00dc[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0x00
[^:]+:[ ]+607f 33cc 55aa cdef 89ab 4567 0123 3210 7654 ba98 00dc[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0x00
[^:]+:[ ]+607f 33cc 55aa cdef 89ab 4567 0123 3210 7654 ba98 fedc[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe
[^:]+:[ ]+607f 33cc 55aa cdef 89ab 4567 0123 3210 7654 ba98 fedc[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe
22 changes: 22 additions & 0 deletions gas/testsuite/gas/riscv/insn.d
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,25 @@ Disassembly of section .text:
[^:]+:[ ]+607f 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+0000 0000 0000 0000 ?
[^:]+:[ ]+0000 0000 0000 ?
[^:]+:[ ]+007f 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
[^:]+:[ ]+8000 ?
[^:]+:[ ]+007f 0000 0000 0000[ ]+\.byte[ ]+0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
[^:]+:[ ]+8000 ?
[^:]+:[ ]+607f 89ab 4567 0123[ ]+\.byte[ ]+0x7f, 0x60, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+3210 7654 ba98 fedc ?
[^:]+:[ ]+0000 0000 0000 ?
[^:]+:[ ]+607f 89ab 4567 0123[ ]+\.byte[ ]+0x7f, 0x60, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
[^:]+:[ ]+3210 7654 ba98 fedc ?
[^:]+:[ ]+0000 0000 0000 ?
[^:]+:[ ]+607f 33cc 55aa cdef[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0x00
[^:]+:[ ]+89ab 4567 0123 3210 ?
[^:]+:[ ]+7654 ba98 00dc ?
[^:]+:[ ]+607f 33cc 55aa cdef[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0x00
[^:]+:[ ]+89ab 4567 0123 3210 ?
[^:]+:[ ]+7654 ba98 00dc ?
[^:]+:[ ]+607f 33cc 55aa cdef[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe
[^:]+:[ ]+89ab 4567 0123 3210 ?
[^:]+:[ ]+7654 ba98 fedc ?
[^:]+:[ ]+607f 33cc 55aa cdef[ ]+\.byte[ ]+0x7f, 0x60, 0xcc, 0x33, 0xaa, 0x55, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe
[^:]+:[ ]+89ab 4567 0123 3210 ?
[^:]+:[ ]+7654 ba98 fedc ?
9 changes: 9 additions & 0 deletions gas/testsuite/gas/riscv/insn.s
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,12 @@ target:
.insn 10, 0x007f
.insn 12, 0x107f
.insn 22, 0x607f

.insn 0x8000000000000000007f
.insn 10, 0x8000000000000000007f
.insn 0x000000000000fedcba98765432100123456789ab607f
.insn 22, 0x000000000000fedcba98765432100123456789ab607f
.insn 0x00dcba98765432100123456789abcdef55aa33cc607f
.insn 22, 0x00dcba98765432100123456789abcdef55aa33cc607f
.insn 0xfedcba98765432100123456789abcdef55aa33cc607f
.insn 22, 0xfedcba98765432100123456789abcdef55aa33cc607f
13 changes: 8 additions & 5 deletions opcodes/riscv-dis.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,10 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
this is little-endian code. */

static int
riscv_disassemble_insn (bfd_vma memaddr, insn_t word, disassemble_info *info)
riscv_disassemble_insn (bfd_vma memaddr,
insn_t word,
const bfd_byte *packet,
disassemble_info *info)
{
const struct riscv_opcode *op;
static bool init = false;
Expand Down Expand Up @@ -806,8 +809,7 @@ riscv_disassemble_insn (bfd_vma memaddr, insn_t word, disassemble_info *info)
", ");
(*info->fprintf_styled_func) (info->stream, dis_style_immediate,
"0x%02x",
(unsigned int) (word & 0xff));
word >>= 8;
(unsigned int) (*packet++));
}
}
break;
Expand Down Expand Up @@ -983,6 +985,7 @@ riscv_data_length (bfd_vma memaddr,
static int
riscv_disassemble_data (bfd_vma memaddr ATTRIBUTE_UNUSED,
insn_t data,
const bfd_byte *packet ATTRIBUTE_UNUSED,
disassemble_info *info)
{
info->display_endian = info->endian;
Expand Down Expand Up @@ -1037,7 +1040,7 @@ print_insn_riscv (bfd_vma memaddr, struct disassemble_info *info)
bfd_vma dump_size;
int status;
enum riscv_seg_mstate mstate;
int (*riscv_disassembler) (bfd_vma, insn_t, struct disassemble_info *);
int (*riscv_disassembler) (bfd_vma, insn_t, const bfd_byte*, struct disassemble_info *);

if (info->disassembler_options != NULL)
{
Expand Down Expand Up @@ -1081,7 +1084,7 @@ print_insn_riscv (bfd_vma memaddr, struct disassemble_info *info)
}
insn = (insn_t) bfd_get_bits (packet, dump_size * 8, false);

return (*riscv_disassembler) (memaddr, insn, info);
return (*riscv_disassembler) (memaddr, insn, packet, info);
}

disassembler_ftype
Expand Down

0 comments on commit ed49fad

Please sign in to comment.