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