cgen-asm.c revision 60484
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/* Set the cgen_parse_operand_fn callback.  */
32
33void
34cgen_set_parse_operand_fn (cd, fn)
35     CGEN_CPU_DESC cd;
36     cgen_parse_operand_fn fn;
37{
38  cd->parse_operand_fn = fn;
39}
40
41/* Called whenever starting to parse an insn.  */
42
43void
44cgen_init_parse_operand (cd)
45     CGEN_CPU_DESC cd;
46{
47  /* This tells the callback to re-initialize.  */
48  (void) (* cd->parse_operand_fn)
49    (cd, CGEN_PARSE_OPERAND_INIT, NULL, 0, 0, NULL, NULL);
50}
51
52/* Subroutine of build_asm_hash_table to add INSNS to the hash table.
53
54   COUNT is the number of elements in INSNS.
55   ENTSIZE is sizeof (CGEN_IBASE) for the target.
56   ??? No longer used but leave in for now.
57   HTABLE points to the hash table.
58   HENTBUF is a pointer to sufficiently large buffer of hash entries.
59   The result is a pointer to the next entry to use.
60
61   The table is scanned backwards as additions are made to the front of the
62   list and we want earlier ones to be prefered.  */
63
64static CGEN_INSN_LIST *
65hash_insn_array (cd, insns, count, entsize, htable, hentbuf)
66     CGEN_CPU_DESC cd;
67     const CGEN_INSN *insns;
68     int count;
69     int entsize;
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
95static CGEN_INSN_LIST *
96hash_insn_list (cd, insns, htable, hentbuf)
97     CGEN_CPU_DESC cd;
98     const CGEN_INSN_LIST *insns;
99     CGEN_INSN_LIST **htable;
100     CGEN_INSN_LIST *hentbuf;
101{
102  const CGEN_INSN_LIST *ilist;
103
104  for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
105    {
106      unsigned int hash;
107
108      if (! (* cd->asm_hash_p) (ilist->insn))
109	continue;
110      hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn));
111      hentbuf->next = htable[hash];
112      hentbuf->insn = ilist->insn;
113      htable[hash] = hentbuf;
114    }
115
116  return hentbuf;
117}
118
119/* Build the assembler instruction hash table.  */
120
121static void
122build_asm_hash_table (cd)
123     CGEN_CPU_DESC cd;
124{
125  int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
126  CGEN_INSN_TABLE *insn_table = &cd->insn_table;
127  CGEN_INSN_TABLE *macro_insn_table = &cd->macro_insn_table;
128  unsigned int hash_size = cd->asm_hash_size;
129  CGEN_INSN_LIST *hash_entry_buf;
130  CGEN_INSN_LIST **asm_hash_table;
131  CGEN_INSN_LIST *asm_hash_table_entries;
132
133  /* The space allocated for the hash table consists of two parts:
134     the hash table and the hash lists.  */
135
136  asm_hash_table = (CGEN_INSN_LIST **)
137    xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
138  memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
139  asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
140    xmalloc (count * sizeof (CGEN_INSN_LIST));
141
142  /* Add compiled in insns.
143     Don't include the first one as it is a reserved entry.  */
144  /* ??? It was the end of all hash chains, and also the special
145     "invalid insn" marker.  May be able to do it differently now.  */
146
147  hash_entry_buf = hash_insn_array (cd,
148				    insn_table->init_entries + 1,
149				    insn_table->num_init_entries - 1,
150				    insn_table->entry_size,
151				    asm_hash_table, hash_entry_buf);
152
153  /* Add compiled in macro-insns.  */
154
155  hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
156				    macro_insn_table->num_init_entries,
157				    macro_insn_table->entry_size,
158				    asm_hash_table, hash_entry_buf);
159
160  /* Add runtime added insns.
161     Later added insns will be prefered over earlier ones.  */
162
163  hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
164				   asm_hash_table, hash_entry_buf);
165
166  /* Add runtime added macro-insns.  */
167
168  hash_insn_list (cd, macro_insn_table->new_entries,
169		  asm_hash_table, hash_entry_buf);
170
171  cd->asm_hash_table = asm_hash_table;
172  cd->asm_hash_table_entries = asm_hash_table_entries;
173}
174
175/* Return the first entry in the hash list for INSN.  */
176
177CGEN_INSN_LIST *
178cgen_asm_lookup_insn (cd, insn)
179     CGEN_CPU_DESC cd;
180     const char *insn;
181{
182  unsigned int hash;
183
184  if (cd->asm_hash_table == NULL)
185    build_asm_hash_table (cd);
186
187  hash = (* cd->asm_hash) (insn);
188  return cd->asm_hash_table[hash];
189}
190
191/* Keyword parser.
192   The result is NULL upon success or an error message.
193   If successful, *STRP is updated to point passed the keyword.
194
195   ??? At present we have a static notion of how to pick out a keyword.
196   Later we can allow a target to customize this if necessary [say by
197   recording something in the keyword table].  */
198
199const char *
200cgen_parse_keyword (cd, strp, keyword_table, valuep)
201     CGEN_CPU_DESC cd;
202     const char **strp;
203     CGEN_KEYWORD *keyword_table;
204     long *valuep;
205{
206  const CGEN_KEYWORD_ENTRY *ke;
207  char buf[256];
208  const char *p,*start;
209
210  p = start = *strp;
211
212  /* Allow any first character.
213     Note that this allows recognizing ",a" for the annul flag in sparc
214     even though "," is subsequently not a valid keyword char.  */
215  if (*p)
216    ++p;
217
218  /* Now allow letters, digits, and _.  */
219  while (((p - start) < (int) sizeof (buf))
220	 && (isalnum ((unsigned char) *p) || *p == '_'))
221    ++p;
222
223  if (p - start >= (int) sizeof (buf))
224    return _("unrecognized keyword/register name");
225
226  memcpy (buf, start, p - start);
227  buf[p - start] = 0;
228
229  ke = cgen_keyword_lookup_name (keyword_table, buf);
230
231  if (ke != NULL)
232    {
233      *valuep = ke->value;
234      /* Don't advance pointer if we recognized the null keyword.  */
235      if (ke->name[0] != 0)
236	*strp = p;
237      return NULL;
238    }
239
240  return "unrecognized keyword/register name";
241}
242
243/* Parse a small signed integer parser.
244   ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
245   Note that if the caller expects a bfd_vma result, it should call
246   cgen_parse_address.  */
247
248const char *
249cgen_parse_signed_integer (cd, strp, opindex, valuep)
250     CGEN_CPU_DESC cd;
251     const char **strp;
252     int opindex;
253     long *valuep;
254{
255  bfd_vma value;
256  enum cgen_parse_operand_result result;
257  const char *errmsg;
258
259  errmsg = (* cd->parse_operand_fn)
260    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
261     &result, &value);
262  /* FIXME: Examine `result'.  */
263  if (!errmsg)
264    *valuep = value;
265  return errmsg;
266}
267
268/* Parse a small unsigned integer parser.
269   ??? VALUEP is not a bfd_vma * on purpose, though this is confusing.
270   Note that if the caller expects a bfd_vma result, it should call
271   cgen_parse_address.  */
272
273const char *
274cgen_parse_unsigned_integer (cd, strp, opindex, valuep)
275     CGEN_CPU_DESC cd;
276     const char **strp;
277     int opindex;
278     unsigned long *valuep;
279{
280  bfd_vma value;
281  enum cgen_parse_operand_result result;
282  const char *errmsg;
283
284  errmsg = (* cd->parse_operand_fn)
285    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
286     &result, &value);
287  /* FIXME: Examine `result'.  */
288  if (!errmsg)
289    *valuep = value;
290  return errmsg;
291}
292
293/* Address parser.  */
294
295const char *
296cgen_parse_address (cd, strp, opindex, opinfo, resultp, valuep)
297     CGEN_CPU_DESC cd;
298     const char **strp;
299     int opindex;
300     int opinfo;
301     enum cgen_parse_operand_result *resultp;
302     bfd_vma *valuep;
303{
304  bfd_vma value;
305  enum cgen_parse_operand_result result_type;
306  const char *errmsg;
307
308  errmsg = (* cd->parse_operand_fn)
309    (cd, CGEN_PARSE_OPERAND_ADDRESS, strp, opindex, opinfo,
310     &result_type, &value);
311  /* FIXME: Examine `result'.  */
312  if (!errmsg)
313    {
314      if (resultp != NULL)
315	*resultp = result_type;
316      *valuep = value;
317    }
318  return errmsg;
319}
320
321/* Signed integer validation routine.  */
322
323const char *
324cgen_validate_signed_integer (value, min, max)
325     long value, min, max;
326{
327  if (value < min || value > max)
328    {
329      static char buf[100];
330
331      /* xgettext:c-format */
332      sprintf (buf, _("operand out of range (%ld not between %ld and %ld)"),
333		      value, min, max);
334      return buf;
335    }
336
337  return NULL;
338}
339
340/* Unsigned integer validation routine.
341   Supplying `min' here may seem unnecessary, but we also want to handle
342   cases where min != 0 (and max > LONG_MAX).  */
343
344const char *
345cgen_validate_unsigned_integer (value, min, max)
346     unsigned long value, min, 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 (%lu not between %lu and %lu)"),
354	       value, min, max);
355      return buf;
356    }
357
358  return NULL;
359}
360