Home | History | Annotate | Line # | Download | only in opcodes
      1   1.1  christos /* CGEN generic assembler support code.
      2  1.10  christos    Copyright (C) 1996-2025 Free Software Foundation, Inc.
      3   1.1  christos 
      4   1.1  christos    This file is part of libopcodes.
      5   1.1  christos 
      6   1.1  christos    This library is free software; you can redistribute it and/or modify
      7   1.1  christos    it under the terms of the GNU General Public License as published by
      8   1.1  christos    the Free Software Foundation; either version 3, or (at your option)
      9   1.1  christos    any later version.
     10   1.1  christos 
     11   1.1  christos    It is distributed in the hope that it will be useful, but WITHOUT
     12   1.1  christos    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     13   1.1  christos    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     14   1.1  christos    License for more details.
     15   1.1  christos 
     16   1.1  christos 
     17   1.1  christos    You should have received a copy of the GNU General Public License along
     18   1.1  christos    with this program; if not, write to the Free Software Foundation, Inc.,
     19   1.1  christos    51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
     20   1.1  christos 
     21   1.1  christos #include "sysdep.h"
     22   1.1  christos #include <stdio.h>
     23   1.1  christos #include "ansidecl.h"
     24   1.1  christos #include "libiberty.h"
     25   1.1  christos #include "safe-ctype.h"
     26   1.1  christos #include "bfd.h"
     27   1.1  christos #include "symcat.h"
     28   1.1  christos #include "opcode/cgen.h"
     29   1.1  christos #include "opintl.h"
     30   1.1  christos 
     31   1.1  christos static CGEN_INSN_LIST *  hash_insn_array      (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
     32   1.1  christos static CGEN_INSN_LIST *  hash_insn_list       (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
     33   1.1  christos static void              build_asm_hash_table (CGEN_CPU_DESC);
     34   1.1  christos 
     35   1.1  christos /* Set the cgen_parse_operand_fn callback.  */
     36   1.1  christos 
     37   1.1  christos void
     38   1.1  christos cgen_set_parse_operand_fn (CGEN_CPU_DESC cd, cgen_parse_operand_fn fn)
     39   1.1  christos {
     40   1.1  christos   cd->parse_operand_fn = fn;
     41   1.1  christos }
     42   1.1  christos 
     43   1.1  christos /* Called whenever starting to parse an insn.  */
     44   1.1  christos 
     45   1.1  christos void
     46   1.1  christos cgen_init_parse_operand (CGEN_CPU_DESC cd)
     47   1.1  christos {
     48   1.1  christos   /* This tells the callback to re-initialize.  */
     49   1.1  christos   (void) (* cd->parse_operand_fn)
     50   1.1  christos     (cd, CGEN_PARSE_OPERAND_INIT, NULL, 0, 0, NULL, NULL);
     51   1.1  christos }
     52   1.1  christos 
     53   1.1  christos /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
     54   1.1  christos 
     55   1.1  christos    COUNT is the number of elements in INSNS.
     56   1.1  christos    ENTSIZE is sizeof (CGEN_IBASE) for the target.
     57   1.1  christos    ??? No longer used but leave in for now.
     58   1.1  christos    HTABLE points to the hash table.
     59   1.1  christos    HENTBUF is a pointer to sufficiently large buffer of hash entries.
     60   1.1  christos    The result is a pointer to the next entry to use.
     61   1.1  christos 
     62   1.1  christos    The table is scanned backwards as additions are made to the front of the
     63   1.8  christos    list and we want earlier ones to be preferred.  */
     64   1.1  christos 
     65   1.1  christos static CGEN_INSN_LIST *
     66   1.1  christos hash_insn_array (CGEN_CPU_DESC cd,
     67   1.1  christos 		 const CGEN_INSN *insns,
     68   1.1  christos 		 int count,
     69   1.1  christos 		 int entsize ATTRIBUTE_UNUSED,
     70   1.1  christos 		 CGEN_INSN_LIST **htable,
     71   1.1  christos 		 CGEN_INSN_LIST *hentbuf)
     72   1.1  christos {
     73   1.1  christos   int i;
     74   1.1  christos 
     75   1.1  christos   for (i = count - 1; i >= 0; --i, ++hentbuf)
     76   1.1  christos     {
     77   1.1  christos       unsigned int hash;
     78   1.1  christos       const CGEN_INSN *insn = &insns[i];
     79   1.1  christos 
     80   1.1  christos       if (! (* cd->asm_hash_p) (insn))
     81   1.1  christos 	continue;
     82   1.1  christos       hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (insn));
     83   1.1  christos       hentbuf->next = htable[hash];
     84   1.1  christos       hentbuf->insn = insn;
     85   1.1  christos       htable[hash] = hentbuf;
     86   1.1  christos     }
     87   1.1  christos 
     88   1.1  christos   return hentbuf;
     89   1.1  christos }
     90   1.1  christos 
     91   1.1  christos /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
     92   1.1  christos    This function is identical to hash_insn_array except the insns are
     93   1.1  christos    in a list.  */
     94   1.1  christos 
     95   1.1  christos static CGEN_INSN_LIST *
     96   1.1  christos hash_insn_list (CGEN_CPU_DESC cd,
     97   1.1  christos 		const CGEN_INSN_LIST *insns,
     98   1.1  christos 		CGEN_INSN_LIST **htable,
     99   1.1  christos 		CGEN_INSN_LIST *hentbuf)
    100   1.1  christos {
    101   1.1  christos   const CGEN_INSN_LIST *ilist;
    102   1.1  christos 
    103   1.1  christos   for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
    104   1.1  christos     {
    105   1.1  christos       unsigned int hash;
    106   1.1  christos 
    107   1.1  christos       if (! (* cd->asm_hash_p) (ilist->insn))
    108   1.1  christos 	continue;
    109   1.1  christos       hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn));
    110   1.1  christos       hentbuf->next = htable[hash];
    111   1.1  christos       hentbuf->insn = ilist->insn;
    112   1.1  christos       htable[hash] = hentbuf;
    113   1.1  christos     }
    114   1.1  christos 
    115   1.1  christos   return hentbuf;
    116   1.1  christos }
    117   1.1  christos 
    118   1.1  christos /* Build the assembler instruction hash table.  */
    119   1.1  christos 
    120   1.1  christos static void
    121   1.1  christos build_asm_hash_table (CGEN_CPU_DESC cd)
    122   1.1  christos {
    123   1.1  christos   int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
    124   1.1  christos   CGEN_INSN_TABLE *insn_table = &cd->insn_table;
    125   1.1  christos   CGEN_INSN_TABLE *macro_insn_table = &cd->macro_insn_table;
    126   1.1  christos   unsigned int hash_size = cd->asm_hash_size;
    127   1.1  christos   CGEN_INSN_LIST *hash_entry_buf;
    128   1.1  christos   CGEN_INSN_LIST **asm_hash_table;
    129   1.1  christos   CGEN_INSN_LIST *asm_hash_table_entries;
    130   1.1  christos 
    131   1.1  christos   /* The space allocated for the hash table consists of two parts:
    132   1.1  christos      the hash table and the hash lists.  */
    133   1.1  christos 
    134   1.1  christos   asm_hash_table = (CGEN_INSN_LIST **)
    135   1.1  christos     xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
    136   1.1  christos   memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
    137   1.1  christos   asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
    138   1.1  christos     xmalloc (count * sizeof (CGEN_INSN_LIST));
    139   1.1  christos 
    140   1.1  christos   /* Add compiled in insns.
    141   1.1  christos      Don't include the first one as it is a reserved entry.  */
    142   1.1  christos   /* ??? It was the end of all hash chains, and also the special
    143   1.1  christos      "invalid insn" marker.  May be able to do it differently now.  */
    144   1.1  christos 
    145   1.1  christos   hash_entry_buf = hash_insn_array (cd,
    146   1.1  christos 				    insn_table->init_entries + 1,
    147   1.1  christos 				    insn_table->num_init_entries - 1,
    148   1.1  christos 				    insn_table->entry_size,
    149   1.1  christos 				    asm_hash_table, hash_entry_buf);
    150   1.1  christos 
    151   1.1  christos   /* Add compiled in macro-insns.  */
    152   1.1  christos 
    153   1.1  christos   hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
    154   1.1  christos 				    macro_insn_table->num_init_entries,
    155   1.1  christos 				    macro_insn_table->entry_size,
    156   1.1  christos 				    asm_hash_table, hash_entry_buf);
    157   1.1  christos 
    158   1.1  christos   /* Add runtime added insns.
    159   1.8  christos      Later added insns will be preferred over earlier ones.  */
    160   1.1  christos 
    161   1.1  christos   hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
    162   1.1  christos 				   asm_hash_table, hash_entry_buf);
    163   1.1  christos 
    164   1.1  christos   /* Add runtime added macro-insns.  */
    165   1.1  christos 
    166   1.1  christos   hash_insn_list (cd, macro_insn_table->new_entries,
    167   1.1  christos 		  asm_hash_table, hash_entry_buf);
    168   1.1  christos 
    169   1.1  christos   cd->asm_hash_table = asm_hash_table;
    170   1.1  christos   cd->asm_hash_table_entries = asm_hash_table_entries;
    171   1.1  christos }
    172   1.1  christos 
    173   1.1  christos /* Return the first entry in the hash list for INSN.  */
    174   1.1  christos 
    175   1.1  christos CGEN_INSN_LIST *
    176   1.1  christos cgen_asm_lookup_insn (CGEN_CPU_DESC cd, const char *insn)
    177   1.1  christos {
    178   1.1  christos   unsigned int hash;
    179   1.1  christos 
    180   1.1  christos   if (cd->asm_hash_table == NULL)
    181   1.1  christos     build_asm_hash_table (cd);
    182   1.1  christos 
    183   1.1  christos   hash = (* cd->asm_hash) (insn);
    184   1.1  christos   return cd->asm_hash_table[hash];
    185   1.1  christos }
    186   1.1  christos 
    187   1.1  christos /* Keyword parser.
    189   1.1  christos    The result is NULL upon success or an error message.
    190   1.1  christos    If successful, *STRP is updated to point passed the keyword.
    191   1.1  christos 
    192   1.1  christos    ??? At present we have a static notion of how to pick out a keyword.
    193   1.1  christos    Later we can allow a target to customize this if necessary [say by
    194   1.1  christos    recording something in the keyword table].  */
    195   1.1  christos 
    196   1.1  christos const char *
    197   1.1  christos cgen_parse_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    198   1.1  christos 		    const char **strp,
    199   1.1  christos 		    CGEN_KEYWORD *keyword_table,
    200   1.1  christos 		    long *valuep)
    201   1.1  christos {
    202   1.1  christos   const CGEN_KEYWORD_ENTRY *ke;
    203   1.1  christos   char buf[256];
    204   1.1  christos   const char *p,*start;
    205   1.1  christos 
    206   1.1  christos   if (keyword_table->name_hash_table == NULL)
    207   1.1  christos     (void) cgen_keyword_search_init (keyword_table, NULL);
    208   1.1  christos 
    209   1.1  christos   p = start = *strp;
    210   1.1  christos 
    211   1.1  christos   /* Allow any first character.  This is to make life easier for
    212   1.1  christos      the fairly common case of suffixes, eg. 'ld.b.w', where the first
    213   1.1  christos      character of the suffix ('.') is special.  */
    214   1.1  christos   if (*p)
    215   1.3  christos     ++p;
    216   1.1  christos 
    217   1.1  christos   /* Allow letters, digits, and any special characters.  */
    218   1.1  christos   while (((p - start) < (int) sizeof (buf))
    219   1.1  christos 	 && *p
    220   1.1  christos 	 && (ISALNUM (*p)
    221   1.1  christos 	     || *p == '_'
    222   1.1  christos 	     || strchr (keyword_table->nonalpha_chars, *p)))
    223   1.1  christos     ++p;
    224   1.1  christos 
    225   1.1  christos   if (p - start >= (int) sizeof (buf))
    226   1.1  christos     {
    227   1.1  christos       /* All non-empty CGEN keywords can fit into BUF.  The only thing
    228   1.1  christos 	 we can match here is the empty keyword.  */
    229   1.1  christos       buf[0] = 0;
    230   1.1  christos     }
    231   1.1  christos   else
    232   1.1  christos     {
    233   1.1  christos       memcpy (buf, start, p - start);
    234   1.1  christos       buf[p - start] = 0;
    235   1.1  christos     }
    236   1.1  christos 
    237   1.1  christos   ke = cgen_keyword_lookup_name (keyword_table, buf);
    238   1.1  christos 
    239   1.1  christos   if (ke != NULL)
    240   1.1  christos     {
    241   1.1  christos       *valuep = ke->value;
    242   1.1  christos       /* Don't advance pointer if we recognized the null keyword.  */
    243   1.1  christos       if (ke->name[0] != 0)
    244   1.1  christos 	*strp = p;
    245   1.1  christos       return NULL;
    246   1.1  christos     }
    247   1.1  christos 
    248   1.1  christos   return "unrecognized keyword/register name";
    249   1.1  christos }
    250   1.1  christos 
    251   1.1  christos /* Parse a small signed integer parser.
    252   1.1  christos    ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
    253   1.1  christos    Note that if the caller expects a bfd_vma result, it should call
    254   1.1  christos    cgen_parse_address.  */
    255   1.1  christos 
    256   1.1  christos const char *
    257   1.1  christos cgen_parse_signed_integer (CGEN_CPU_DESC cd,
    258   1.1  christos 			   const char **strp,
    259   1.1  christos 			   int opindex,
    260   1.1  christos 			   long *valuep)
    261   1.1  christos {
    262   1.1  christos   bfd_vma value;
    263   1.1  christos   enum cgen_parse_operand_result result;
    264   1.1  christos   const char *errmsg;
    265   1.1  christos 
    266   1.1  christos   errmsg = (* cd->parse_operand_fn)
    267   1.1  christos     (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
    268   1.1  christos      &result, &value);
    269   1.1  christos   /* FIXME: Examine `result'.  */
    270   1.1  christos   if (!errmsg)
    271   1.1  christos     {
    272   1.1  christos       /* Handle the case where a hex value is parsed on a 64-bit host.
    273   1.1  christos 	 A value like 0xffffe000 is clearly intended to be a negative
    274   1.1  christos 	 16-bit value, but on a 64-bit host it will be parsed by gas
    275   1.1  christos 	 as 0x00000000ffffe000.
    276   1.1  christos 
    277   1.1  christos 	 The shifts below are designed not to produce compile time
    278   1.1  christos 	 warnings on a 32-bit host.  */
    279   1.1  christos       if (sizeof (value) > 4
    280   1.1  christos 	  && result == CGEN_PARSE_OPERAND_RESULT_NUMBER
    281   1.1  christos 	  && value > 0
    282   1.1  christos 	  && (value & 0x80000000)
    283   1.3  christos 	  && ((value >> 31) == 1))
    284   1.1  christos 	value |= ((bfd_vma) -1) << 31;
    285   1.1  christos 
    286   1.1  christos       *valuep = value;
    287   1.1  christos     }
    288   1.1  christos   return errmsg;
    289   1.1  christos }
    290   1.1  christos 
    291   1.1  christos /* Parse a small unsigned integer parser.
    292   1.1  christos    ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
    293   1.1  christos    Note that if the caller expects a bfd_vma result, it should call
    294   1.1  christos    cgen_parse_address.  */
    295   1.1  christos 
    296   1.1  christos const char *
    297   1.1  christos cgen_parse_unsigned_integer (CGEN_CPU_DESC cd,
    298   1.1  christos 			     const char **strp,
    299   1.1  christos 			     int opindex,
    300   1.1  christos 			     unsigned long *valuep)
    301   1.1  christos {
    302   1.1  christos   bfd_vma value;
    303   1.1  christos   enum cgen_parse_operand_result result;
    304   1.1  christos   const char *errmsg;
    305   1.1  christos 
    306   1.1  christos   errmsg = (* cd->parse_operand_fn)
    307   1.1  christos     (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
    308   1.1  christos      &result, &value);
    309   1.1  christos   /* FIXME: Examine `result'.  */
    310   1.1  christos   if (!errmsg)
    311   1.1  christos     *valuep = value;
    312   1.1  christos   return errmsg;
    313   1.1  christos }
    314   1.1  christos 
    315   1.1  christos /* Address parser.  */
    316   1.1  christos 
    317   1.1  christos const char *
    318   1.1  christos cgen_parse_address (CGEN_CPU_DESC cd,
    319   1.1  christos 		    const char **strp,
    320   1.1  christos 		    int opindex,
    321   1.1  christos 		    int opinfo,
    322   1.1  christos 		    enum cgen_parse_operand_result *resultp,
    323   1.1  christos 		    bfd_vma *valuep)
    324   1.1  christos {
    325   1.1  christos   bfd_vma value;
    326   1.1  christos   enum cgen_parse_operand_result result_type;
    327   1.1  christos   const char *errmsg;
    328   1.1  christos 
    329   1.1  christos   errmsg = (* cd->parse_operand_fn)
    330   1.1  christos     (cd, CGEN_PARSE_OPERAND_ADDRESS, strp, opindex, opinfo,
    331   1.1  christos      &result_type, &value);
    332   1.1  christos   /* FIXME: Examine `result'.  */
    333   1.1  christos   if (!errmsg)
    334   1.1  christos     {
    335   1.1  christos       if (resultp != NULL)
    336   1.1  christos 	*resultp = result_type;
    337   1.1  christos       *valuep = value;
    338   1.1  christos     }
    339   1.1  christos   return errmsg;
    340   1.1  christos }
    341   1.1  christos 
    342   1.1  christos /* Signed integer validation routine.  */
    344   1.1  christos 
    345   1.1  christos const char *
    346   1.1  christos cgen_validate_signed_integer (long value, long min, long max)
    347   1.1  christos {
    348   1.1  christos   if (value < min || value > max)
    349   1.1  christos     {
    350   1.1  christos       static char buf[100];
    351   1.1  christos 
    352   1.1  christos       /* xgettext:c-format */
    353   1.1  christos       sprintf (buf, _("operand out of range (%ld not between %ld and %ld)"),
    354   1.1  christos 		      value, min, max);
    355   1.1  christos       return buf;
    356   1.1  christos     }
    357   1.1  christos 
    358   1.1  christos   return NULL;
    359   1.1  christos }
    360   1.1  christos 
    361   1.1  christos /* Unsigned integer validation routine.
    362   1.1  christos    Supplying `min' here may seem unnecessary, but we also want to handle
    363   1.1  christos    cases where min != 0 (and max > LONG_MAX).  */
    364   1.1  christos 
    365   1.1  christos const char *
    366   1.1  christos cgen_validate_unsigned_integer (unsigned long value,
    367   1.1  christos 				unsigned long min,
    368   1.1  christos 				unsigned long max)
    369   1.1  christos {
    370   1.1  christos   if (value < min || value > max)
    371   1.1  christos     {
    372   1.1  christos       static char buf[100];
    373   1.1  christos 
    374   1.1  christos       /* xgettext:c-format */
    375   1.1  christos       sprintf (buf, _("operand out of range (%lu not between %lu and %lu)"),
    376   1.1  christos 	       value, min, max);
    377   1.1  christos       return buf;
    378   1.1  christos     }
    379   1.1  christos 
    380                   return NULL;
    381                 }
    382