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