aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2016-03-28 14:27:43 +0100
committerAndrew Burgess <andrew.burgess@embecosm.com>2016-04-07 18:35:08 +0100
commitda5be03929920c8aed305f9ffac19ce37177b3c6 (patch)
treea6470c609f5a3f5727677a97d5bb8892cfadfaf2 /gas/config
parentc8064e7e9b4d7d9cc29a613af899a7739df60be6 (diff)
downloadgdb-da5be03929920c8aed305f9ffac19ce37177b3c6.zip
gdb-da5be03929920c8aed305f9ffac19ce37177b3c6.tar.gz
gdb-da5be03929920c8aed305f9ffac19ce37177b3c6.tar.bz2
gas/arc: Modify structure used to hold opcodes
The arc assembler builds a hash table to hold references to arc_opcode entries in the arc_opcodes table. This hash assumes that each mnemonic will always appear in a contiguous blocks within the arc_opcodes table; all ADD instruction will be together, all AND instructions will likewise be together and so on. The problem with this is that as different variations of arc are added, then it is often more convenient to split instructions apart, so all the base ADD instructions are together, but, variants of ADD specific to one variation of arc are grouped with other instructions specific to that arc variant. The current data structures don't support splitting the instructions in this way. This commit is a first step towards addressing this limitation. In this commit the hash table that currently holds arc_opcode pointers directly, instead holds a pointer to a new, intermediate, data structure. This new data structure holds the pointer to the arc_opcode. In this way, we can, in the future support having the intermediate structure hold multiple pointers to different arc_opcode groups. There should be no visible functional change after this commit. gas/ChangeLog: * config/tc-arc.c (struct arc_opcode_hash_entry): New structure. (arc_find_opcode): New function. (find_special_case_pseudo): Use arc_find_opcode. (find_special_case_flag): Likewise. (assemble_tokens): Likewise. (md_begin): Build hash using struct arc_opcode_hash_entry.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-arc.c66
1 files changed, 56 insertions, 10 deletions
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
index ea34645..71c8f25 100644
--- a/gas/config/tc-arc.c
+++ b/gas/config/tc-arc.c
@@ -308,6 +308,16 @@ static struct arc_last_insn
bfd_boolean has_delay_slot;
} arc_last_insns[2];
+/* Structure to hold an entry in ARC_OPCODE_HASH. */
+struct arc_opcode_hash_entry
+{
+ /* The number of pointers in the OPCODE list. */
+ size_t count;
+
+ /* Points to a list of opcode pointers. */
+ const struct arc_opcode **opcode;
+};
+
/* Forward declaration. */
static void assemble_insn
(const struct arc_opcode *, const expressionS *, int,
@@ -554,6 +564,29 @@ static bfd_boolean assembling_insn = FALSE;
/* Functions implementation. */
+/* Return a pointer to the first entry in ARC_OPCODE_HASH that matches
+ NAME, or NULL if there are no matching entries. */
+
+static const struct arc_opcode *
+arc_find_opcode (const char *name)
+{
+ const struct arc_opcode_hash_entry *entry;
+ const struct arc_opcode *opcode;
+
+ entry = hash_find (arc_opcode_hash, name);
+ if (entry != NULL)
+ {
+ if (entry->count > 1)
+ as_fatal (_("unable to lookup `%s', too many opcode chains"),
+ name);
+ opcode = *entry->opcode;
+ }
+ else
+ opcode = NULL;
+
+ return opcode;
+}
+
/* Like md_number_to_chars but used for limms. The 4-byte limm value,
is encoded as 'middle-endian' for a little-endian target. FIXME!
this function is used for regular 4 byte instructions as well. */
@@ -1932,8 +1965,7 @@ find_special_case_pseudo (const char *opname,
break;
}
- return (const struct arc_opcode *)
- hash_find (arc_opcode_hash, pseudo_insn->mnemonic_r);
+ return arc_find_opcode (pseudo_insn->mnemonic_r);
}
static const struct arc_opcode *
@@ -1969,9 +2001,7 @@ find_special_case_flag (const char *opname,
flaglen = strlen (flagnm);
if (strcmp (opname + oplen, flagnm) == 0)
{
- opcode = (const struct arc_opcode *)
- hash_find (arc_opcode_hash,
- arc_flag_special_opcode->name);
+ opcode = arc_find_opcode (arc_flag_special_opcode->name);
if (*nflgs + 1 > MAX_INSN_FLGS)
break;
@@ -2019,7 +2049,7 @@ assemble_tokens (const char *opname,
int cpumatch = 1;
/* Search opcodes. */
- opcode = (const struct arc_opcode *) hash_find (arc_opcode_hash, opname);
+ opcode = arc_find_opcode (opname);
/* Couldn't find opcode conventional way, try special cases. */
if (!opcode)
@@ -2164,12 +2194,28 @@ md_begin (void)
for (i = 0; i < arc_num_opcodes;)
{
const char *name, *retval;
+ struct arc_opcode_hash_entry *entry;
name = arc_opcodes[i].name;
- retval = hash_insert (arc_opcode_hash, name, (void *) &arc_opcodes[i]);
- if (retval)
- as_fatal (_("internal error: can't hash opcode '%s': %s"),
- name, retval);
+
+ entry = hash_find (arc_opcode_hash, name);
+ if (entry == NULL)
+ {
+ entry = xmalloc (sizeof (*entry));
+ entry->count = 0;
+ entry->opcode = NULL;
+
+ retval = hash_insert (arc_opcode_hash, name, (void *) entry);
+ if (retval)
+ as_fatal (_("internal error: can't hash opcode '%s': %s"),
+ name, retval);
+ }
+
+ entry->opcode = xrealloc (entry->opcode,
+ sizeof (const struct arc_opcode *)
+ * entry->count + 1);
+ entry->opcode [entry->count] = &arc_opcodes[i];
+ entry->count++;
while (++i < arc_num_opcodes
&& (arc_opcodes[i].name == name