* cgen-asm.in (insert_normal): Use CGEN_BOOL_ATTR.
[platform/upstream/binutils.git] / opcodes / cgen-asm.c
1 /* CGEN generic assembler support code.
2
3    Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
4
5    This file is part of the GNU Binutils and GDB, the GNU debugger.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
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    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #include "sysdep.h"
22 #include <stdio.h>
23 #include <ctype.h>
24 #include "ansidecl.h"
25 #include "libiberty.h"
26 #include "bfd.h"
27 #include "symcat.h"
28 #include "opcode/cgen.h"
29 #include "opintl.h"
30
31 /* Operand parsing callback.  */
32 const char * (*cgen_parse_operand_fn)
33      PARAMS ((enum cgen_parse_operand_type, const char **, int, int,
34               enum cgen_parse_operand_result *, bfd_vma *));
35
36 /* This is not published as part of the public interface so we don't
37    declare this in cgen.h.  */
38 extern CGEN_OPCODE_TABLE *cgen_current_opcode_table;
39
40 /* Assembler instruction hash table.  */
41 static CGEN_INSN_LIST **asm_hash_table;
42 static CGEN_INSN_LIST *asm_hash_table_entries;
43
44 /* Called once at startup and whenever machine/endian change.  */
45
46 void
47 cgen_asm_init ()
48 {
49   if (asm_hash_table)
50     {
51       free (asm_hash_table);
52       free (asm_hash_table_entries);
53       asm_hash_table = NULL;
54       asm_hash_table_entries = NULL;
55     }
56 }
57
58 /* Called whenever starting to parse an insn.  */
59
60 void
61 cgen_init_parse_operand ()
62 {
63   /* This tells the callback to re-initialize.  */
64   (void) (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INIT, NULL, 0, 0,
65                                    NULL, NULL);
66 }
67
68 /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
69
70    COUNT is the number of elements in INSNS.
71    ENTSIZE is sizeof (CGEN_INSN) for the target.
72    This is a bit tricky as the size of the attribute member of CGEN_INSN
73    is variable among architectures.  This code could be moved to
74    cgen-asm.in, but I prefer to keep it here for now.
75    OTABLE is the opcode table.
76    HTABLE points to the hash table.
77    HENTBUF is a pointer to sufficiently large buffer of hash entries.
78    The result is a pointer to the next entry to use.
79
80    The table is scanned backwards as additions are made to the front of the
81    list and we want earlier ones to be prefered.  */
82
83 static CGEN_INSN_LIST *
84 hash_insn_array (insns, count, entsize, otable, htable, hentbuf)
85      const CGEN_INSN * insns;
86      int count;
87      int entsize;
88      const CGEN_OPCODE_TABLE * otable;
89      CGEN_INSN_LIST ** htable;
90      CGEN_INSN_LIST * hentbuf;
91 {
92   const CGEN_INSN * insn;
93
94   for (insn = (CGEN_INSN *) ((char *) insns + entsize * (count - 1));
95        insn >= insns;
96        insn = (CGEN_INSN *) ((char *) insn - entsize), ++ hentbuf)
97     {
98       unsigned int hash;
99
100       if (! (*otable->asm_hash_p) (insn))
101         continue;
102       hash = (*otable->asm_hash) (CGEN_INSN_MNEMONIC (insn));
103       hentbuf->next = htable[hash];
104       hentbuf->insn = insn;
105       htable[hash] = hentbuf;
106     }
107
108   return hentbuf;
109 }
110
111 /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
112    This function is identical to hash_insn_array except the insns are
113    in a list.  */
114
115 static CGEN_INSN_LIST *
116 hash_insn_list (insns, otable, htable, hentbuf)
117      const CGEN_INSN_LIST * insns;
118      const CGEN_OPCODE_TABLE * otable;
119      CGEN_INSN_LIST ** htable;
120      CGEN_INSN_LIST * hentbuf;
121 {
122   const CGEN_INSN_LIST * ilist;
123
124   for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
125     {
126       unsigned int hash;
127
128       if (! (*otable->asm_hash_p) (ilist->insn))
129         continue;
130       hash = (*otable->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn));
131       hentbuf->next = htable[hash];
132       hentbuf->insn = ilist->insn;
133       asm_hash_table[hash] = hentbuf;
134     }
135
136   return hentbuf;
137 }
138
139 /* Build the assembler instruction hash table.  */
140
141 static void
142 build_asm_hash_table ()
143 {
144   int count = cgen_insn_count () + cgen_macro_insn_count ();
145   CGEN_OPCODE_TABLE *opcode_table = cgen_current_opcode_table;
146   CGEN_INSN_TABLE *insn_table = opcode_table->insn_table;
147   CGEN_INSN_TABLE *macro_insn_table = opcode_table->macro_insn_table;
148   unsigned int hash_size = opcode_table->asm_hash_table_size;
149   CGEN_INSN_LIST *hash_entry_buf;
150
151   /* The space allocated for the hash table consists of two parts:
152      the hash table and the hash lists.  */
153
154   asm_hash_table = (CGEN_INSN_LIST **)
155     xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
156   memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
157   asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
158     xmalloc (count * sizeof (CGEN_INSN_LIST));
159
160   /* Add compiled in insns.
161      Don't include the first one as it is a reserved entry.  */
162   /* ??? It was the end of all hash chains, and also the special
163      "illegal insn" marker.  May be able to do it differently now.  */
164
165   hash_entry_buf = hash_insn_array ((CGEN_INSN *) ((char *) insn_table->init_entries
166                                                    + insn_table->entry_size),
167                                     insn_table->num_init_entries - 1,
168                                     insn_table->entry_size,
169                                     opcode_table, asm_hash_table, hash_entry_buf);
170
171   /* Add compiled in macro-insns.  */
172
173   hash_entry_buf = hash_insn_array (macro_insn_table->init_entries,
174                                     macro_insn_table->num_init_entries,
175                                     macro_insn_table->entry_size,
176                                     opcode_table, asm_hash_table, hash_entry_buf);
177
178   /* Add runtime added insns.
179      Later added insns will be prefered over earlier ones.  */
180
181   hash_entry_buf = hash_insn_list (insn_table->new_entries, opcode_table,
182                                    asm_hash_table, hash_entry_buf);
183
184   /* Add runtime added macro-insns.  */
185
186   hash_insn_list (macro_insn_table->new_entries,
187                   opcode_table, asm_hash_table, hash_entry_buf);
188 }
189
190 /* Return the first entry in the hash list for INSN.
191    ??? Of course it would be better to pass in a pointer to the
192    opcode data structure, rather than reference a global.  Later.  */
193
194 CGEN_INSN_LIST *
195 cgen_asm_lookup_insn (insn)
196      const char *insn;
197 {
198   unsigned int hash;
199
200   if (asm_hash_table == NULL)
201     build_asm_hash_table ();
202
203   hash = (*cgen_current_opcode_table->asm_hash) (insn);
204   return asm_hash_table[hash];
205 }
206 \f
207 /* Keyword parser.
208    The result is NULL upon success or an error message.
209    If successful, *STRP is updated to point passed the keyword.
210
211    ??? At present we have a static notion of how to pick out a keyword.
212    Later we can allow a target to customize this if necessary [say by
213    recording something in the keyword table].  */
214
215 const char *
216 cgen_parse_keyword (strp, keyword_table, valuep)
217      const char **strp;
218      CGEN_KEYWORD *keyword_table;
219      long *valuep;
220 {
221   const CGEN_KEYWORD_ENTRY *ke;
222   char buf[256];
223   const char *p,*start;
224
225   p = start = *strp;
226
227   /* Allow any first character.
228      Note that this allows recognizing ",a" for the annul flag in sparc
229      even though "," is subsequently not a valid keyword char.  */
230   if (*p)
231     ++p;
232
233   /* Now allow letters, digits, and _.  */
234   while (((p - start) < (int) sizeof (buf))
235          && (isalnum ((unsigned char) *p) || *p == '_'))
236     ++p;
237
238   if (p - start >= (int) sizeof (buf))
239     return _("unrecognized keyword/register name");
240
241   memcpy (buf, start, p - start);
242   buf[p - start] = 0;
243
244   ke = cgen_keyword_lookup_name (keyword_table, buf);
245
246   if (ke != NULL)
247     {
248       *valuep = ke->value;
249       /* Don't advance pointer if we recognized the null keyword.  */
250       if (ke->name[0] != 0)
251         *strp = p;
252       return NULL;
253     }
254
255   return "unrecognized keyword/register name";
256 }
257
258 /* Signed integer parser.  */
259
260 const char *
261 cgen_parse_signed_integer (strp, opindex, valuep)
262      const char **strp;
263      int opindex;
264      long *valuep;
265 {
266   bfd_vma value;
267   enum cgen_parse_operand_result result;
268   const char *errmsg;
269
270   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
271                                      opindex, BFD_RELOC_NONE,
272                                      &result, &value);
273   /* FIXME: Examine `result'.  */
274   if (!errmsg)
275     *valuep = value;
276   return errmsg;
277 }
278
279 /* Unsigned integer parser.  */
280
281 const char *
282 cgen_parse_unsigned_integer (strp, opindex, valuep)
283      const char **strp;
284      int opindex;
285      unsigned long *valuep;
286 {
287   bfd_vma value;
288   enum cgen_parse_operand_result result;
289   const char *errmsg;
290
291   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
292                                      opindex, BFD_RELOC_NONE,
293                                      &result, &value);
294   /* FIXME: Examine `result'.  */
295   if (!errmsg)
296     *valuep = value;
297   return errmsg;
298 }
299
300 /* Address parser.  */
301
302 const char *
303 cgen_parse_address (strp, opindex, opinfo, resultp, valuep)
304      const char **strp;
305      int opindex;
306      int opinfo;
307      enum cgen_parse_operand_result *resultp;
308      long *valuep;
309 {
310   bfd_vma value;
311   enum cgen_parse_operand_result result_type;
312   const char *errmsg;
313
314   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_ADDRESS, strp,
315                                      opindex, opinfo,
316                                      &result_type, &value);
317   /* FIXME: Examine `result'.  */
318   if (!errmsg)
319     {
320       if (resultp != NULL)
321         *resultp = result_type;
322       *valuep = value;
323     }
324   return errmsg;
325 }
326 \f
327 /* Signed integer validation routine.  */
328
329 const char *
330 cgen_validate_signed_integer (value, min, max)
331      long value, min, max;
332 {
333   if (value < min || value > max)
334     {
335       static char buf[100];
336
337       /* xgettext:c-format */
338       sprintf (buf, _("operand out of range (%ld not between %ld and %ld)"),
339                       value, min, max);
340       return buf;
341     }
342
343   return NULL;
344 }
345
346 /* Unsigned integer validation routine.
347    Supplying `min' here may seem unnecessary, but we also want to handle
348    cases where min != 0 (and max > LONG_MAX).  */
349
350 const char *
351 cgen_validate_unsigned_integer (value, min, max)
352      unsigned long value, min, max;
353 {
354   if (value < min || value > max)
355     {
356       static char buf[100];
357
358       sprintf (buf, _("operand out of range (%lu not between %lu and %lu)"),
359                value, min, max);
360       return buf;
361     }
362
363   return NULL;
364 }