Back to index

cell-binutils  2.17cvs20070401
cgen-dis.c
Go to the documentation of this file.
00001 /* CGEN generic disassembler support code.
00002 
00003    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
00004    Free Software Foundation, Inc.
00005 
00006    This file is part of the GNU Binutils and GDB, the GNU debugger.
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2, or (at your option)
00011    any later version.
00012 
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017 
00018    You should have received a copy of the GNU General Public License along
00019    with this program; if not, write to the Free Software Foundation, Inc.,
00020    51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
00021 
00022 #include "sysdep.h"
00023 #include <stdio.h>
00024 #include "ansidecl.h"
00025 #include "libiberty.h"
00026 #include "bfd.h"
00027 #include "symcat.h"
00028 #include "opcode/cgen.h"
00029 
00030 static CGEN_INSN_LIST *  hash_insn_array        (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
00031 static CGEN_INSN_LIST *  hash_insn_list         (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
00032 static void              build_dis_hash_table   (CGEN_CPU_DESC);
00033 static int            count_decodable_bits   (const CGEN_INSN *);
00034 static void           add_insn_to_hash_chain (CGEN_INSN_LIST *,
00035                                            const CGEN_INSN *,
00036                                            CGEN_INSN_LIST **,
00037                                            unsigned int);
00038 
00039 /* Return the number of decodable bits in this insn.  */
00040 static int
00041 count_decodable_bits (const CGEN_INSN *insn)
00042 {
00043   unsigned mask = CGEN_INSN_BASE_MASK (insn);
00044   int bits = 0;
00045   int m;
00046   for (m = 1; m != 0; m <<= 1)
00047     {
00048       if (mask & m)
00049        ++bits;
00050     }
00051   return bits;
00052 }
00053 
00054 /* Add an instruction to the hash chain.  */     
00055 static void
00056 add_insn_to_hash_chain (CGEN_INSN_LIST *hentbuf,
00057                      const CGEN_INSN *insn,
00058                      CGEN_INSN_LIST **htable,
00059                      unsigned int hash)
00060 {
00061   CGEN_INSN_LIST *current_buf;
00062   CGEN_INSN_LIST *previous_buf;
00063   int insn_decodable_bits;
00064 
00065   /* Add insns sorted by the number of decodable bits, in decreasing order.
00066      This ensures that any insn which is a special case of another will be
00067      checked first.  */
00068   insn_decodable_bits = count_decodable_bits (insn);
00069   previous_buf = NULL;
00070   for (current_buf = htable[hash]; current_buf != NULL;
00071        current_buf = current_buf->next)
00072     {
00073       int current_decodable_bits = count_decodable_bits (current_buf->insn);
00074       if (insn_decodable_bits >= current_decodable_bits)
00075        break;
00076       previous_buf = current_buf;
00077     }
00078 
00079   /* Now insert the new insn.  */
00080   hentbuf->insn = insn;
00081   hentbuf->next = current_buf;
00082   if (previous_buf == NULL)
00083     htable[hash] = hentbuf;
00084   else
00085     previous_buf->next = hentbuf;
00086 }
00087 
00088 /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
00089 
00090    COUNT is the number of elements in INSNS.
00091    ENTSIZE is sizeof (CGEN_IBASE) for the target.
00092    ??? No longer used but leave in for now.
00093    HTABLE points to the hash table.
00094    HENTBUF is a pointer to sufficiently large buffer of hash entries.
00095    The result is a pointer to the next entry to use.
00096 
00097    The table is scanned backwards as additions are made to the front of the
00098    list and we want earlier ones to be prefered.  */
00099 
00100 static CGEN_INSN_LIST *
00101 hash_insn_array (CGEN_CPU_DESC cd,
00102                const CGEN_INSN * insns,
00103                int count,
00104                int entsize ATTRIBUTE_UNUSED,
00105                CGEN_INSN_LIST ** htable,
00106                CGEN_INSN_LIST * hentbuf)
00107 {
00108   int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
00109   int i;
00110 
00111   for (i = count - 1; i >= 0; --i, ++hentbuf)
00112     {
00113       unsigned int hash;
00114       char buf [4];
00115       unsigned long value;
00116       const CGEN_INSN *insn = &insns[i];
00117 
00118       if (! (* cd->dis_hash_p) (insn))
00119        continue;
00120 
00121       /* We don't know whether the target uses the buffer or the base insn
00122         to hash on, so set both up.  */
00123 
00124       value = CGEN_INSN_BASE_VALUE (insn);
00125       bfd_put_bits ((bfd_vma) value,
00126                   buf,
00127                   CGEN_INSN_MASK_BITSIZE (insn),
00128                   big_p);
00129       hash = (* cd->dis_hash) (buf, value);
00130       add_insn_to_hash_chain (hentbuf, insn, htable, hash);
00131     }
00132 
00133   return hentbuf;
00134 }
00135 
00136 /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
00137    This function is identical to hash_insn_array except the insns are
00138    in a list.  */
00139 
00140 static CGEN_INSN_LIST *
00141 hash_insn_list (CGEN_CPU_DESC cd,
00142               const CGEN_INSN_LIST *insns,
00143               CGEN_INSN_LIST **htable,
00144               CGEN_INSN_LIST *hentbuf)
00145 {
00146   int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
00147   const CGEN_INSN_LIST *ilist;
00148 
00149   for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
00150     {
00151       unsigned int hash;
00152       char buf[4];
00153       unsigned long value;
00154 
00155       if (! (* cd->dis_hash_p) (ilist->insn))
00156        continue;
00157 
00158       /* We don't know whether the target uses the buffer or the base insn
00159         to hash on, so set both up.  */
00160 
00161       value = CGEN_INSN_BASE_VALUE (ilist->insn);
00162       bfd_put_bits((bfd_vma) value,
00163                  buf,
00164                  CGEN_INSN_MASK_BITSIZE (ilist->insn),
00165                  big_p);
00166       hash = (* cd->dis_hash) (buf, value);
00167       add_insn_to_hash_chain (hentbuf, ilist->insn, htable, hash);
00168     }
00169 
00170   return hentbuf;
00171 }
00172 
00173 /* Build the disassembler instruction hash table.  */
00174 
00175 static void
00176 build_dis_hash_table (CGEN_CPU_DESC cd)
00177 {
00178   int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
00179   CGEN_INSN_TABLE *insn_table = & cd->insn_table;
00180   CGEN_INSN_TABLE *macro_insn_table = & cd->macro_insn_table;
00181   unsigned int hash_size = cd->dis_hash_size;
00182   CGEN_INSN_LIST *hash_entry_buf;
00183   CGEN_INSN_LIST **dis_hash_table;
00184   CGEN_INSN_LIST *dis_hash_table_entries;
00185 
00186   /* The space allocated for the hash table consists of two parts:
00187      the hash table and the hash lists.  */
00188 
00189   dis_hash_table = (CGEN_INSN_LIST **)
00190     xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
00191   memset (dis_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
00192   dis_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
00193     xmalloc (count * sizeof (CGEN_INSN_LIST));
00194 
00195   /* Add compiled in insns.
00196      Don't include the first one as it is a reserved entry.  */
00197   /* ??? It was the end of all hash chains, and also the special
00198      "invalid insn" marker.  May be able to do it differently now.  */
00199 
00200   hash_entry_buf = hash_insn_array (cd,
00201                                 insn_table->init_entries + 1,
00202                                 insn_table->num_init_entries - 1,
00203                                 insn_table->entry_size,
00204                                 dis_hash_table, hash_entry_buf);
00205 
00206   /* Add compiled in macro-insns.  */
00207 
00208   hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
00209                                 macro_insn_table->num_init_entries,
00210                                 macro_insn_table->entry_size,
00211                                 dis_hash_table, hash_entry_buf);
00212 
00213   /* Add runtime added insns.
00214      Later added insns will be prefered over earlier ones.  */
00215 
00216   hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
00217                                dis_hash_table, hash_entry_buf);
00218 
00219   /* Add runtime added macro-insns.  */
00220 
00221   hash_insn_list (cd, macro_insn_table->new_entries,
00222                 dis_hash_table, hash_entry_buf);
00223 
00224   cd->dis_hash_table = dis_hash_table;
00225   cd->dis_hash_table_entries = dis_hash_table_entries;
00226 }
00227 
00228 /* Return the first entry in the hash list for INSN.  */
00229 
00230 CGEN_INSN_LIST *
00231 cgen_dis_lookup_insn (CGEN_CPU_DESC cd, const char * buf, CGEN_INSN_INT value)
00232 {
00233   unsigned int hash;
00234 
00235   if (cd->dis_hash_table == NULL)
00236     build_dis_hash_table (cd);
00237 
00238   hash = (* cd->dis_hash) (buf, value);
00239 
00240   return cd->dis_hash_table[hash];
00241 }