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