xref: /openbsd-src/gnu/usr.bin/binutils-2.17/opcodes/cgen-dis.c (revision 3d8817e467ea46cf4772788d6804dd293abfb01a)
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