133965Sjdp/* CGEN generic assembler support code.
233965Sjdp
3218822Sdim   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
4218822Sdim   Free Software Foundation, Inc.
533965Sjdp
638889Sjdp   This file is part of the GNU Binutils and GDB, the GNU debugger.
733965Sjdp
838889Sjdp   This program is free software; you can redistribute it and/or modify
938889Sjdp   it under the terms of the GNU General Public License as published by
1038889Sjdp   the Free Software Foundation; either version 2, or (at your option)
1138889Sjdp   any later version.
1233965Sjdp
1338889Sjdp   This program is distributed in the hope that it will be useful,
1438889Sjdp   but WITHOUT ANY WARRANTY; without even the implied warranty of
1538889Sjdp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1638889Sjdp   GNU General Public License for more details.
1733965Sjdp
1838889Sjdp   You should have received a copy of the GNU General Public License along
1938889Sjdp   with this program; if not, write to the Free Software Foundation, Inc.,
20218822Sdim   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2133965Sjdp
2233965Sjdp#include "sysdep.h"
2333965Sjdp#include <stdio.h>
2433965Sjdp#include "ansidecl.h"
2533965Sjdp#include "libiberty.h"
2689857Sobrien#include "safe-ctype.h"
2733965Sjdp#include "bfd.h"
2838889Sjdp#include "symcat.h"
2933965Sjdp#include "opcode/cgen.h"
3060484Sobrien#include "opintl.h"
3133965Sjdp
32130561Sobrienstatic CGEN_INSN_LIST *  hash_insn_array      (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
33130561Sobrienstatic CGEN_INSN_LIST *  hash_insn_list       (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
34130561Sobrienstatic void              build_asm_hash_table (CGEN_CPU_DESC);
3589857Sobrien
3660484Sobrien/* Set the cgen_parse_operand_fn callback.  */
3733965Sjdp
3860484Sobrienvoid
39130561Sobriencgen_set_parse_operand_fn (CGEN_CPU_DESC cd, cgen_parse_operand_fn fn)
4060484Sobrien{
4160484Sobrien  cd->parse_operand_fn = fn;
4260484Sobrien}
4333965Sjdp
4460484Sobrien/* Called whenever starting to parse an insn.  */
4533965Sjdp
4633965Sjdpvoid
47130561Sobriencgen_init_parse_operand (CGEN_CPU_DESC cd)
4833965Sjdp{
4960484Sobrien  /* This tells the callback to re-initialize.  */
5060484Sobrien  (void) (* cd->parse_operand_fn)
5160484Sobrien    (cd, CGEN_PARSE_OPERAND_INIT, NULL, 0, 0, NULL, NULL);
5260484Sobrien}
5360484Sobrien
5460484Sobrien/* Subroutine of build_asm_hash_table to add INSNS to the hash table.
5560484Sobrien
5660484Sobrien   COUNT is the number of elements in INSNS.
5760484Sobrien   ENTSIZE is sizeof (CGEN_IBASE) for the target.
5860484Sobrien   ??? No longer used but leave in for now.
5960484Sobrien   HTABLE points to the hash table.
6060484Sobrien   HENTBUF is a pointer to sufficiently large buffer of hash entries.
6160484Sobrien   The result is a pointer to the next entry to use.
6260484Sobrien
6360484Sobrien   The table is scanned backwards as additions are made to the front of the
6460484Sobrien   list and we want earlier ones to be prefered.  */
6560484Sobrien
6660484Sobrienstatic CGEN_INSN_LIST *
67130561Sobrienhash_insn_array (CGEN_CPU_DESC cd,
68130561Sobrien		 const CGEN_INSN *insns,
69130561Sobrien		 int count,
70130561Sobrien		 int entsize ATTRIBUTE_UNUSED,
71130561Sobrien		 CGEN_INSN_LIST **htable,
72130561Sobrien		 CGEN_INSN_LIST *hentbuf)
7360484Sobrien{
7460484Sobrien  int i;
7560484Sobrien
7660484Sobrien  for (i = count - 1; i >= 0; --i, ++hentbuf)
7733965Sjdp    {
7860484Sobrien      unsigned int hash;
7960484Sobrien      const CGEN_INSN *insn = &insns[i];
8060484Sobrien
8160484Sobrien      if (! (* cd->asm_hash_p) (insn))
8260484Sobrien	continue;
8360484Sobrien      hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (insn));
8460484Sobrien      hentbuf->next = htable[hash];
8560484Sobrien      hentbuf->insn = insn;
8660484Sobrien      htable[hash] = hentbuf;
8733965Sjdp    }
8860484Sobrien
8960484Sobrien  return hentbuf;
9033965Sjdp}
9133965Sjdp
9260484Sobrien/* Subroutine of build_asm_hash_table to add INSNS to the hash table.
9360484Sobrien   This function is identical to hash_insn_array except the insns are
9460484Sobrien   in a list.  */
9533965Sjdp
9660484Sobrienstatic CGEN_INSN_LIST *
97130561Sobrienhash_insn_list (CGEN_CPU_DESC cd,
98130561Sobrien		const CGEN_INSN_LIST *insns,
99130561Sobrien		CGEN_INSN_LIST **htable,
100130561Sobrien		CGEN_INSN_LIST *hentbuf)
10133965Sjdp{
10260484Sobrien  const CGEN_INSN_LIST *ilist;
10360484Sobrien
10460484Sobrien  for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
10560484Sobrien    {
10660484Sobrien      unsigned int hash;
10760484Sobrien
10860484Sobrien      if (! (* cd->asm_hash_p) (ilist->insn))
10960484Sobrien	continue;
11060484Sobrien      hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn));
11160484Sobrien      hentbuf->next = htable[hash];
11260484Sobrien      hentbuf->insn = ilist->insn;
11360484Sobrien      htable[hash] = hentbuf;
11460484Sobrien    }
11560484Sobrien
11660484Sobrien  return hentbuf;
11733965Sjdp}
11833965Sjdp
11933965Sjdp/* Build the assembler instruction hash table.  */
12033965Sjdp
12133965Sjdpstatic void
122130561Sobrienbuild_asm_hash_table (CGEN_CPU_DESC cd)
12333965Sjdp{
12460484Sobrien  int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
12560484Sobrien  CGEN_INSN_TABLE *insn_table = &cd->insn_table;
12660484Sobrien  CGEN_INSN_TABLE *macro_insn_table = &cd->macro_insn_table;
12760484Sobrien  unsigned int hash_size = cd->asm_hash_size;
12860484Sobrien  CGEN_INSN_LIST *hash_entry_buf;
12960484Sobrien  CGEN_INSN_LIST **asm_hash_table;
13060484Sobrien  CGEN_INSN_LIST *asm_hash_table_entries;
13133965Sjdp
13233965Sjdp  /* The space allocated for the hash table consists of two parts:
13333965Sjdp     the hash table and the hash lists.  */
13433965Sjdp
13533965Sjdp  asm_hash_table = (CGEN_INSN_LIST **)
13660484Sobrien    xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
13760484Sobrien  memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
13860484Sobrien  asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
13960484Sobrien    xmalloc (count * sizeof (CGEN_INSN_LIST));
14033965Sjdp
14133965Sjdp  /* Add compiled in insns.
14260484Sobrien     Don't include the first one as it is a reserved entry.  */
14360484Sobrien  /* ??? It was the end of all hash chains, and also the special
14460484Sobrien     "invalid insn" marker.  May be able to do it differently now.  */
14533965Sjdp
14660484Sobrien  hash_entry_buf = hash_insn_array (cd,
14760484Sobrien				    insn_table->init_entries + 1,
14860484Sobrien				    insn_table->num_init_entries - 1,
14960484Sobrien				    insn_table->entry_size,
15060484Sobrien				    asm_hash_table, hash_entry_buf);
15133965Sjdp
15260484Sobrien  /* Add compiled in macro-insns.  */
15360484Sobrien
15460484Sobrien  hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
15560484Sobrien				    macro_insn_table->num_init_entries,
15660484Sobrien				    macro_insn_table->entry_size,
15760484Sobrien				    asm_hash_table, hash_entry_buf);
15860484Sobrien
15933965Sjdp  /* Add runtime added insns.
16060484Sobrien     Later added insns will be prefered over earlier ones.  */
16160484Sobrien
16260484Sobrien  hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
16360484Sobrien				   asm_hash_table, hash_entry_buf);
16460484Sobrien
16560484Sobrien  /* Add runtime added macro-insns.  */
16660484Sobrien
16760484Sobrien  hash_insn_list (cd, macro_insn_table->new_entries,
16860484Sobrien		  asm_hash_table, hash_entry_buf);
16960484Sobrien
17060484Sobrien  cd->asm_hash_table = asm_hash_table;
17160484Sobrien  cd->asm_hash_table_entries = asm_hash_table_entries;
17233965Sjdp}
17333965Sjdp
17433965Sjdp/* Return the first entry in the hash list for INSN.  */
17533965Sjdp
17633965SjdpCGEN_INSN_LIST *
177130561Sobriencgen_asm_lookup_insn (CGEN_CPU_DESC cd, const char *insn)
17833965Sjdp{
17933965Sjdp  unsigned int hash;
18033965Sjdp
18160484Sobrien  if (cd->asm_hash_table == NULL)
18260484Sobrien    build_asm_hash_table (cd);
18333965Sjdp
18460484Sobrien  hash = (* cd->asm_hash) (insn);
18560484Sobrien  return cd->asm_hash_table[hash];
18633965Sjdp}
18733965Sjdp
18833965Sjdp/* Keyword parser.
18933965Sjdp   The result is NULL upon success or an error message.
19033965Sjdp   If successful, *STRP is updated to point passed the keyword.
19133965Sjdp
19233965Sjdp   ??? At present we have a static notion of how to pick out a keyword.
19333965Sjdp   Later we can allow a target to customize this if necessary [say by
19433965Sjdp   recording something in the keyword table].  */
19533965Sjdp
19633965Sjdpconst char *
197130561Sobriencgen_parse_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
198130561Sobrien		    const char **strp,
199130561Sobrien		    CGEN_KEYWORD *keyword_table,
200130561Sobrien		    long *valuep)
20133965Sjdp{
20238889Sjdp  const CGEN_KEYWORD_ENTRY *ke;
20333965Sjdp  char buf[256];
20438889Sjdp  const char *p,*start;
20533965Sjdp
20689857Sobrien  if (keyword_table->name_hash_table == NULL)
20789857Sobrien    (void) cgen_keyword_search_init (keyword_table, NULL);
20889857Sobrien
20938889Sjdp  p = start = *strp;
21033965Sjdp
21189857Sobrien  /* Allow any first character.  This is to make life easier for
21289857Sobrien     the fairly common case of suffixes, eg. 'ld.b.w', where the first
21389857Sobrien     character of the suffix ('.') is special.  */
21433965Sjdp  if (*p)
21533965Sjdp    ++p;
21689857Sobrien
21789857Sobrien  /* Allow letters, digits, and any special characters.  */
21838889Sjdp  while (((p - start) < (int) sizeof (buf))
21989857Sobrien	 && *p
220130561Sobrien	 && (ISALNUM (*p)
221130561Sobrien	     || *p == '_'
222130561Sobrien	     || strchr (keyword_table->nonalpha_chars, *p)))
22333965Sjdp    ++p;
22433965Sjdp
22538889Sjdp  if (p - start >= (int) sizeof (buf))
22689857Sobrien    {
22789857Sobrien      /* All non-empty CGEN keywords can fit into BUF.  The only thing
22889857Sobrien	 we can match here is the empty keyword.  */
22989857Sobrien      buf[0] = 0;
23089857Sobrien    }
23189857Sobrien  else
23289857Sobrien    {
23389857Sobrien      memcpy (buf, start, p - start);
23489857Sobrien      buf[p - start] = 0;
23589857Sobrien    }
23633965Sjdp
23733965Sjdp  ke = cgen_keyword_lookup_name (keyword_table, buf);
23833965Sjdp
23933965Sjdp  if (ke != NULL)
24033965Sjdp    {
24133965Sjdp      *valuep = ke->value;
24238889Sjdp      /* Don't advance pointer if we recognized the null keyword.  */
24338889Sjdp      if (ke->name[0] != 0)
24438889Sjdp	*strp = p;
24533965Sjdp      return NULL;
24633965Sjdp    }
24733965Sjdp
24833965Sjdp  return "unrecognized keyword/register name";
24933965Sjdp}
25033965Sjdp
25160484Sobrien/* Parse a small signed integer parser.
25260484Sobrien   ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
25360484Sobrien   Note that if the caller expects a bfd_vma result, it should call
25460484Sobrien   cgen_parse_address.  */
25533965Sjdp
25633965Sjdpconst char *
257130561Sobriencgen_parse_signed_integer (CGEN_CPU_DESC cd,
258130561Sobrien			   const char **strp,
259130561Sobrien			   int opindex,
260130561Sobrien			   long *valuep)
26133965Sjdp{
26238889Sjdp  bfd_vma value;
26333965Sjdp  enum cgen_parse_operand_result result;
26433965Sjdp  const char *errmsg;
26533965Sjdp
26660484Sobrien  errmsg = (* cd->parse_operand_fn)
26760484Sobrien    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
26860484Sobrien     &result, &value);
26933965Sjdp  /* FIXME: Examine `result'.  */
27033965Sjdp  if (!errmsg)
27138889Sjdp    *valuep = value;
27233965Sjdp  return errmsg;
27333965Sjdp}
27433965Sjdp
27560484Sobrien/* Parse a small unsigned integer parser.
27660484Sobrien   ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
27760484Sobrien   Note that if the caller expects a bfd_vma result, it should call
27860484Sobrien   cgen_parse_address.  */
27933965Sjdp
28033965Sjdpconst char *
281130561Sobriencgen_parse_unsigned_integer (CGEN_CPU_DESC cd,
282130561Sobrien			     const char **strp,
283130561Sobrien			     int opindex,
284130561Sobrien			     unsigned long *valuep)
28533965Sjdp{
28638889Sjdp  bfd_vma value;
28733965Sjdp  enum cgen_parse_operand_result result;
28833965Sjdp  const char *errmsg;
28933965Sjdp
29060484Sobrien  errmsg = (* cd->parse_operand_fn)
29160484Sobrien    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
29260484Sobrien     &result, &value);
29333965Sjdp  /* FIXME: Examine `result'.  */
29433965Sjdp  if (!errmsg)
29538889Sjdp    *valuep = value;
29633965Sjdp  return errmsg;
29733965Sjdp}
29833965Sjdp
29933965Sjdp/* Address parser.  */
30033965Sjdp
30133965Sjdpconst char *
302130561Sobriencgen_parse_address (CGEN_CPU_DESC cd,
303130561Sobrien		    const char **strp,
304130561Sobrien		    int opindex,
305130561Sobrien		    int opinfo,
306130561Sobrien		    enum cgen_parse_operand_result *resultp,
307130561Sobrien		    bfd_vma *valuep)
30833965Sjdp{
30938889Sjdp  bfd_vma value;
31038889Sjdp  enum cgen_parse_operand_result result_type;
31133965Sjdp  const char *errmsg;
31233965Sjdp
31360484Sobrien  errmsg = (* cd->parse_operand_fn)
31460484Sobrien    (cd, CGEN_PARSE_OPERAND_ADDRESS, strp, opindex, opinfo,
31560484Sobrien     &result_type, &value);
31633965Sjdp  /* FIXME: Examine `result'.  */
31733965Sjdp  if (!errmsg)
31833965Sjdp    {
31938889Sjdp      if (resultp != NULL)
32038889Sjdp	*resultp = result_type;
32133965Sjdp      *valuep = value;
32233965Sjdp    }
32333965Sjdp  return errmsg;
32433965Sjdp}
32533965Sjdp
32633965Sjdp/* Signed integer validation routine.  */
32733965Sjdp
32833965Sjdpconst char *
329130561Sobriencgen_validate_signed_integer (long value, long min, long max)
33033965Sjdp{
33133965Sjdp  if (value < min || value > max)
33233965Sjdp    {
33333965Sjdp      static char buf[100];
33433965Sjdp
33560484Sobrien      /* xgettext:c-format */
33660484Sobrien      sprintf (buf, _("operand out of range (%ld not between %ld and %ld)"),
33760484Sobrien		      value, min, max);
33833965Sjdp      return buf;
33933965Sjdp    }
34033965Sjdp
34133965Sjdp  return NULL;
34233965Sjdp}
34333965Sjdp
34433965Sjdp/* Unsigned integer validation routine.
34533965Sjdp   Supplying `min' here may seem unnecessary, but we also want to handle
34633965Sjdp   cases where min != 0 (and max > LONG_MAX).  */
34733965Sjdp
34833965Sjdpconst char *
349130561Sobriencgen_validate_unsigned_integer (unsigned long value,
350130561Sobrien				unsigned long min,
351130561Sobrien				unsigned long max)
35233965Sjdp{
35333965Sjdp  if (value < min || value > max)
35433965Sjdp    {
35533965Sjdp      static char buf[100];
35633965Sjdp
35760484Sobrien      /* xgettext:c-format */
35860484Sobrien      sprintf (buf, _("operand out of range (%lu not between %lu and %lu)"),
35960484Sobrien	       value, min, max);
36033965Sjdp      return buf;
36133965Sjdp    }
36233965Sjdp
36333965Sjdp  return NULL;
36433965Sjdp}
365