Skip to content

Commit

Permalink
RISC-V: Better support for long instructions (assembler)
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 and
3.  Because long opcode was handled separately (from struct riscv_cl_insn),
    some information like DWARF line number correspondence was missing.

To resolve these problems, this commit:

1.  Handles bignum (and its encodings) precisely and
2.  Incorporates long opcode handling into regular instruction handling.

This commit will be tested on the separate commit.

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
	maximum length.
  • Loading branch information
a4lg committed Nov 28, 2022
1 parent 97f006b commit 634001b
Showing 1 changed file with 32 additions and 9 deletions.
41 changes: 32 additions & 9 deletions gas/config/tc-riscv.c
Original file line number Diff line number Diff line change
@@ -42,9 +42,13 @@ struct riscv_cl_insn
/* The opcode's entry in riscv_opcodes. */
const struct riscv_opcode *insn_mo;

/* The encoded instruction bits. */
/* The encoded instruction bits
(first bits enough to extract instruction length on a long opcode). */
insn_t insn_opcode;

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

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

@@ -720,6 +724,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;
insn->frag = NULL;
insn->where = 0;
insn->fixp = NULL;
@@ -731,7 +736,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
@@ -3503,7 +3511,9 @@ 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 32-bits of a big number.
Assume that generic_bignum_to_int32 work on such number. */
values[num++] = (insn_t) generic_bignum_to_int32 ();
break;
default:
/* The first value isn't constant, so it should be
@@ -3530,12 +3540,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;
}

@@ -4612,7 +4635,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);

0 comments on commit 634001b

Please sign in to comment.