1fddef416Sniklas /* CGEN generic disassembler support code.
2fddef416Sniklas
3d2201f2fSdrahn Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002
45f210c2aSfgsch Free Software Foundation, Inc.
5fddef416Sniklas
6fddef416Sniklas This file is part of the GNU Binutils and GDB, the GNU debugger.
7fddef416Sniklas
8fddef416Sniklas This program is free software; you can redistribute it and/or modify
9fddef416Sniklas it under the terms of the GNU General Public License as published by
10fddef416Sniklas the Free Software Foundation; either version 2, or (at your option)
11fddef416Sniklas any later version.
12fddef416Sniklas
13fddef416Sniklas This program is distributed in the hope that it will be useful,
14fddef416Sniklas but WITHOUT ANY WARRANTY; without even the implied warranty of
15fddef416Sniklas MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16fddef416Sniklas GNU General Public License for more details.
17fddef416Sniklas
18fddef416Sniklas You should have received a copy of the GNU General Public License along
19fddef416Sniklas with this program; if not, write to the Free Software Foundation, Inc.,
20fddef416Sniklas 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21fddef416Sniklas
22fddef416Sniklas #include "sysdep.h"
23fddef416Sniklas #include <stdio.h>
24fddef416Sniklas #include "ansidecl.h"
25fddef416Sniklas #include "libiberty.h"
26fddef416Sniklas #include "bfd.h"
27f7cc78ecSespie #include "symcat.h"
28fddef416Sniklas #include "opcode/cgen.h"
29fddef416Sniklas
30*cf2f2c56Smiod static CGEN_INSN_LIST * hash_insn_array (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
31*cf2f2c56Smiod static CGEN_INSN_LIST * hash_insn_list (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
32*cf2f2c56Smiod static void build_dis_hash_table (CGEN_CPU_DESC);
33*cf2f2c56Smiod static int count_decodable_bits (const CGEN_INSN *);
34*cf2f2c56Smiod static void add_insn_to_hash_chain (CGEN_INSN_LIST *,
35d2201f2fSdrahn const CGEN_INSN *,
36d2201f2fSdrahn CGEN_INSN_LIST **,
37*cf2f2c56Smiod unsigned int);
38d2201f2fSdrahn
39d2201f2fSdrahn /* Return the number of decodable bits in this insn. */
40d2201f2fSdrahn static int
count_decodable_bits(const CGEN_INSN * insn)41*cf2f2c56Smiod count_decodable_bits (const CGEN_INSN *insn)
42d2201f2fSdrahn {
43d2201f2fSdrahn unsigned mask = CGEN_INSN_BASE_MASK (insn);
44d2201f2fSdrahn int bits = 0;
45d2201f2fSdrahn int m;
46d2201f2fSdrahn for (m = 1; m != 0; m <<= 1)
47d2201f2fSdrahn {
48d2201f2fSdrahn if (mask & m)
49d2201f2fSdrahn ++bits;
50d2201f2fSdrahn }
51d2201f2fSdrahn return bits;
52d2201f2fSdrahn }
53d2201f2fSdrahn
54d2201f2fSdrahn /* Add an instruction to the hash chain. */
55d2201f2fSdrahn static void
add_insn_to_hash_chain(CGEN_INSN_LIST * hentbuf,const CGEN_INSN * insn,CGEN_INSN_LIST ** htable,unsigned int hash)56*cf2f2c56Smiod add_insn_to_hash_chain (CGEN_INSN_LIST *hentbuf,
57*cf2f2c56Smiod const CGEN_INSN *insn,
58*cf2f2c56Smiod CGEN_INSN_LIST **htable,
59*cf2f2c56Smiod unsigned int hash)
60d2201f2fSdrahn {
61d2201f2fSdrahn CGEN_INSN_LIST *current_buf;
62d2201f2fSdrahn CGEN_INSN_LIST *previous_buf;
63d2201f2fSdrahn int insn_decodable_bits;
64d2201f2fSdrahn
65d2201f2fSdrahn /* Add insns sorted by the number of decodable bits, in decreasing order.
66d2201f2fSdrahn This ensures that any insn which is a special case of another will be
67d2201f2fSdrahn checked first. */
68d2201f2fSdrahn insn_decodable_bits = count_decodable_bits (insn);
69d2201f2fSdrahn previous_buf = NULL;
70d2201f2fSdrahn for (current_buf = htable[hash]; current_buf != NULL;
71d2201f2fSdrahn current_buf = current_buf->next)
72d2201f2fSdrahn {
73d2201f2fSdrahn int current_decodable_bits = count_decodable_bits (current_buf->insn);
74d2201f2fSdrahn if (insn_decodable_bits >= current_decodable_bits)
75d2201f2fSdrahn break;
76d2201f2fSdrahn previous_buf = current_buf;
77d2201f2fSdrahn }
78d2201f2fSdrahn
79d2201f2fSdrahn /* Now insert the new insn. */
80d2201f2fSdrahn hentbuf->insn = insn;
81d2201f2fSdrahn hentbuf->next = current_buf;
82d2201f2fSdrahn if (previous_buf == NULL)
83d2201f2fSdrahn htable[hash] = hentbuf;
84d2201f2fSdrahn else
85d2201f2fSdrahn previous_buf->next = hentbuf;
86d2201f2fSdrahn }
87d2201f2fSdrahn
88f7cc78ecSespie /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
89fddef416Sniklas
90f7cc78ecSespie COUNT is the number of elements in INSNS.
91f7cc78ecSespie ENTSIZE is sizeof (CGEN_IBASE) for the target.
92f7cc78ecSespie ??? No longer used but leave in for now.
93f7cc78ecSespie HTABLE points to the hash table.
94f7cc78ecSespie HENTBUF is a pointer to sufficiently large buffer of hash entries.
95f7cc78ecSespie The result is a pointer to the next entry to use.
96fddef416Sniklas
97f7cc78ecSespie The table is scanned backwards as additions are made to the front of the
98f7cc78ecSespie list and we want earlier ones to be prefered. */
99f7cc78ecSespie
100f7cc78ecSespie static CGEN_INSN_LIST *
hash_insn_array(CGEN_CPU_DESC cd,const CGEN_INSN * insns,int count,int entsize ATTRIBUTE_UNUSED,CGEN_INSN_LIST ** htable,CGEN_INSN_LIST * hentbuf)101*cf2f2c56Smiod hash_insn_array (CGEN_CPU_DESC cd,
102*cf2f2c56Smiod const CGEN_INSN * insns,
103*cf2f2c56Smiod int count,
104*cf2f2c56Smiod int entsize ATTRIBUTE_UNUSED,
105*cf2f2c56Smiod CGEN_INSN_LIST ** htable,
106*cf2f2c56Smiod CGEN_INSN_LIST * hentbuf)
107fddef416Sniklas {
108f7cc78ecSespie int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
109f7cc78ecSespie int i;
110f7cc78ecSespie
111f7cc78ecSespie for (i = count - 1; i >= 0; --i, ++hentbuf)
112fddef416Sniklas {
113f7cc78ecSespie unsigned int hash;
114f7cc78ecSespie char buf [4];
115f7cc78ecSespie unsigned long value;
116f7cc78ecSespie const CGEN_INSN *insn = &insns[i];
117f7cc78ecSespie
118f7cc78ecSespie if (! (* cd->dis_hash_p) (insn))
119f7cc78ecSespie continue;
120f7cc78ecSespie
121f7cc78ecSespie /* We don't know whether the target uses the buffer or the base insn
122f7cc78ecSespie to hash on, so set both up. */
123f7cc78ecSespie
124f7cc78ecSespie value = CGEN_INSN_BASE_VALUE (insn);
1255f210c2aSfgsch bfd_put_bits ((bfd_vma) value,
1265f210c2aSfgsch buf,
1275f210c2aSfgsch CGEN_INSN_MASK_BITSIZE (insn),
1285f210c2aSfgsch big_p);
129f7cc78ecSespie hash = (* cd->dis_hash) (buf, value);
130d2201f2fSdrahn add_insn_to_hash_chain (hentbuf, insn, htable, hash);
131f7cc78ecSespie }
132f7cc78ecSespie
133f7cc78ecSespie return hentbuf;
134f7cc78ecSespie }
135f7cc78ecSespie
136f7cc78ecSespie /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
137f7cc78ecSespie This function is identical to hash_insn_array except the insns are
138f7cc78ecSespie in a list. */
139f7cc78ecSespie
140f7cc78ecSespie static CGEN_INSN_LIST *
hash_insn_list(CGEN_CPU_DESC cd,const CGEN_INSN_LIST * insns,CGEN_INSN_LIST ** htable,CGEN_INSN_LIST * hentbuf)141*cf2f2c56Smiod hash_insn_list (CGEN_CPU_DESC cd,
142*cf2f2c56Smiod const CGEN_INSN_LIST *insns,
143*cf2f2c56Smiod CGEN_INSN_LIST **htable,
144*cf2f2c56Smiod CGEN_INSN_LIST *hentbuf)
145f7cc78ecSespie {
146f7cc78ecSespie int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
147f7cc78ecSespie const CGEN_INSN_LIST *ilist;
148f7cc78ecSespie
149f7cc78ecSespie for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
150f7cc78ecSespie {
151f7cc78ecSespie unsigned int hash;
152f7cc78ecSespie char buf[4];
153f7cc78ecSespie unsigned long value;
154f7cc78ecSespie
155f7cc78ecSespie if (! (* cd->dis_hash_p) (ilist->insn))
156f7cc78ecSespie continue;
157f7cc78ecSespie
158f7cc78ecSespie /* We don't know whether the target uses the buffer or the base insn
159f7cc78ecSespie to hash on, so set both up. */
160f7cc78ecSespie
161f7cc78ecSespie value = CGEN_INSN_BASE_VALUE (ilist->insn);
1625f210c2aSfgsch bfd_put_bits((bfd_vma) value,
1635f210c2aSfgsch buf,
1645f210c2aSfgsch CGEN_INSN_MASK_BITSIZE (ilist->insn),
1655f210c2aSfgsch big_p);
166f7cc78ecSespie hash = (* cd->dis_hash) (buf, value);
167d2201f2fSdrahn add_insn_to_hash_chain (hentbuf, ilist->insn, htable, hash);
168f7cc78ecSespie }
169f7cc78ecSespie
170f7cc78ecSespie return hentbuf;
171fddef416Sniklas }
172fddef416Sniklas
173fddef416Sniklas /* Build the disassembler instruction hash table. */
174fddef416Sniklas
175fddef416Sniklas static void
build_dis_hash_table(CGEN_CPU_DESC cd)176*cf2f2c56Smiod build_dis_hash_table (CGEN_CPU_DESC cd)
177fddef416Sniklas {
178f7cc78ecSespie int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
179f7cc78ecSespie CGEN_INSN_TABLE *insn_table = & cd->insn_table;
180f7cc78ecSespie CGEN_INSN_TABLE *macro_insn_table = & cd->macro_insn_table;
181f7cc78ecSespie unsigned int hash_size = cd->dis_hash_size;
182f7cc78ecSespie CGEN_INSN_LIST *hash_entry_buf;
183f7cc78ecSespie CGEN_INSN_LIST **dis_hash_table;
184f7cc78ecSespie CGEN_INSN_LIST *dis_hash_table_entries;
185fddef416Sniklas
186fddef416Sniklas /* The space allocated for the hash table consists of two parts:
187fddef416Sniklas the hash table and the hash lists. */
188fddef416Sniklas
189fddef416Sniklas dis_hash_table = (CGEN_INSN_LIST **)
190f7cc78ecSespie xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
191f7cc78ecSespie memset (dis_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
192f7cc78ecSespie dis_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
193f7cc78ecSespie xmalloc (count * sizeof (CGEN_INSN_LIST));
194fddef416Sniklas
195fddef416Sniklas /* Add compiled in insns.
196f7cc78ecSespie Don't include the first one as it is a reserved entry. */
197f7cc78ecSespie /* ??? It was the end of all hash chains, and also the special
198f7cc78ecSespie "invalid insn" marker. May be able to do it differently now. */
199fddef416Sniklas
200f7cc78ecSespie hash_entry_buf = hash_insn_array (cd,
201f7cc78ecSespie insn_table->init_entries + 1,
202f7cc78ecSespie insn_table->num_init_entries - 1,
203f7cc78ecSespie insn_table->entry_size,
204f7cc78ecSespie dis_hash_table, hash_entry_buf);
205f7cc78ecSespie
206f7cc78ecSespie /* Add compiled in macro-insns. */
207f7cc78ecSespie
208f7cc78ecSespie hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
209f7cc78ecSespie macro_insn_table->num_init_entries,
210f7cc78ecSespie macro_insn_table->entry_size,
211f7cc78ecSespie dis_hash_table, hash_entry_buf);
212fddef416Sniklas
213fddef416Sniklas /* Add runtime added insns.
214f7cc78ecSespie Later added insns will be prefered over earlier ones. */
215f7cc78ecSespie
216f7cc78ecSespie hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
217f7cc78ecSespie dis_hash_table, hash_entry_buf);
218f7cc78ecSespie
219f7cc78ecSespie /* Add runtime added macro-insns. */
220f7cc78ecSespie
221f7cc78ecSespie hash_insn_list (cd, macro_insn_table->new_entries,
222f7cc78ecSespie dis_hash_table, hash_entry_buf);
223f7cc78ecSespie
224f7cc78ecSespie cd->dis_hash_table = dis_hash_table;
225f7cc78ecSespie cd->dis_hash_table_entries = dis_hash_table_entries;
226fddef416Sniklas }
227fddef416Sniklas
228fddef416Sniklas /* Return the first entry in the hash list for INSN. */
229fddef416Sniklas
230fddef416Sniklas CGEN_INSN_LIST *
cgen_dis_lookup_insn(CGEN_CPU_DESC cd,const char * buf,CGEN_INSN_INT value)231*cf2f2c56Smiod cgen_dis_lookup_insn (CGEN_CPU_DESC cd, const char * buf, CGEN_INSN_INT value)
232fddef416Sniklas {
233fddef416Sniklas unsigned int hash;
234fddef416Sniklas
235f7cc78ecSespie if (cd->dis_hash_table == NULL)
236f7cc78ecSespie build_dis_hash_table (cd);
237fddef416Sniklas
238f7cc78ecSespie hash = (* cd->dis_hash) (buf, value);
239f7cc78ecSespie
240f7cc78ecSespie return cd->dis_hash_table[hash];
241fddef416Sniklas }
242