cgen-dis.c revision 130561
182794Sobrien/* CGEN generic disassembler support code.
282794Sobrien
382794Sobrien   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002
482794Sobrien   Free Software Foundation, Inc.
582794Sobrien
682794Sobrien   This file is part of the GNU Binutils and GDB, the GNU debugger.
782794Sobrien
882794Sobrien   This program is free software; you can redistribute it and/or modify
982794Sobrien   it under the terms of the GNU General Public License as published by
1082794Sobrien   the Free Software Foundation; either version 2, or (at your option)
1182794Sobrien   any later version.
1282794Sobrien
1382794Sobrien   This program is distributed in the hope that it will be useful,
1482794Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1582794Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1682794Sobrien   GNU General Public License for more details.
1782794Sobrien
1882794Sobrien   You should have received a copy of the GNU General Public License along
1982794Sobrien   with this program; if not, write to the Free Software Foundation, Inc.,
2082794Sobrien   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2182794Sobrien
2282794Sobrien#include "sysdep.h"
2382794Sobrien#include <stdio.h>
2482794Sobrien#include "ansidecl.h"
2582794Sobrien#include "libiberty.h"
2682794Sobrien#include "bfd.h"
2782794Sobrien#include "symcat.h"
2882794Sobrien#include "opcode/cgen.h"
2982794Sobrien
3082794Sobrienstatic CGEN_INSN_LIST *  hash_insn_array        (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
3182794Sobrienstatic CGEN_INSN_LIST *  hash_insn_list         (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
3282794Sobrienstatic void              build_dis_hash_table   (CGEN_CPU_DESC);
3382794Sobrienstatic int		 count_decodable_bits   (const CGEN_INSN *);
3482794Sobrienstatic void		 add_insn_to_hash_chain (CGEN_INSN_LIST *,
3582794Sobrien						 const CGEN_INSN *,
3682794Sobrien						 CGEN_INSN_LIST **,
3782794Sobrien						 unsigned int);
3882794Sobrien
3982794Sobrien/* Return the number of decodable bits in this insn.  */
4082794Sobrienstatic int
4182794Sobriencount_decodable_bits (const CGEN_INSN *insn)
4282794Sobrien{
4382794Sobrien  unsigned mask = CGEN_INSN_BASE_MASK (insn);
4482794Sobrien  int bits = 0;
4582794Sobrien  int m;
4682794Sobrien  for (m = 1; m != 0; m <<= 1)
4782794Sobrien    {
4882794Sobrien      if (mask & m)
4982794Sobrien	++bits;
5082794Sobrien    }
5182794Sobrien  return bits;
5282794Sobrien}
5382794Sobrien
5482794Sobrien/* Add an instruction to the hash chain.  */
5582794Sobrienstatic void
5682794Sobrienadd_insn_to_hash_chain (CGEN_INSN_LIST *hentbuf,
5782794Sobrien			const CGEN_INSN *insn,
5882794Sobrien			CGEN_INSN_LIST **htable,
5982794Sobrien			unsigned int hash)
6082794Sobrien{
6182794Sobrien  CGEN_INSN_LIST *current_buf;
6282794Sobrien  CGEN_INSN_LIST *previous_buf;
6382794Sobrien  int insn_decodable_bits;
6482794Sobrien
6582794Sobrien  /* Add insns sorted by the number of decodable bits, in decreasing order.
6682794Sobrien     This ensures that any insn which is a special case of another will be
6782794Sobrien     checked first.  */
6882794Sobrien  insn_decodable_bits = count_decodable_bits (insn);
6982794Sobrien  previous_buf = NULL;
7082794Sobrien  for (current_buf = htable[hash]; current_buf != NULL;
7182794Sobrien       current_buf = current_buf->next)
7282794Sobrien    {
7382794Sobrien      int current_decodable_bits = count_decodable_bits (current_buf->insn);
7482794Sobrien      if (insn_decodable_bits >= current_decodable_bits)
7582794Sobrien	break;
7682794Sobrien      previous_buf = current_buf;
7782794Sobrien    }
7882794Sobrien
7982794Sobrien  /* Now insert the new insn.  */
8082794Sobrien  hentbuf->insn = insn;
8182794Sobrien  hentbuf->next = current_buf;
8282794Sobrien  if (previous_buf == NULL)
8382794Sobrien    htable[hash] = hentbuf;
8482794Sobrien  else
8582794Sobrien    previous_buf->next = hentbuf;
8682794Sobrien}
8782794Sobrien
8882794Sobrien/* Subroutine of build_dis_hash_table to add INSNS to the hash table.
8982794Sobrien
9082794Sobrien   COUNT is the number of elements in INSNS.
9182794Sobrien   ENTSIZE is sizeof (CGEN_IBASE) for the target.
9282794Sobrien   ??? No longer used but leave in for now.
9382794Sobrien   HTABLE points to the hash table.
9482794Sobrien   HENTBUF is a pointer to sufficiently large buffer of hash entries.
9582794Sobrien   The result is a pointer to the next entry to use.
9682794Sobrien
9782794Sobrien   The table is scanned backwards as additions are made to the front of the
9882794Sobrien   list and we want earlier ones to be prefered.  */
9982794Sobrien
10082794Sobrienstatic CGEN_INSN_LIST *
10182794Sobrienhash_insn_array (CGEN_CPU_DESC cd,
10282794Sobrien		 const CGEN_INSN * insns,
10382794Sobrien		 int count,
10482794Sobrien		 int entsize ATTRIBUTE_UNUSED,
10582794Sobrien		 CGEN_INSN_LIST ** htable,
10682794Sobrien		 CGEN_INSN_LIST * hentbuf)
10782794Sobrien{
10882794Sobrien  int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
10982794Sobrien  int i;
11082794Sobrien
11182794Sobrien  for (i = count - 1; i >= 0; --i, ++hentbuf)
11282794Sobrien    {
11382794Sobrien      unsigned int hash;
11482794Sobrien      char buf [4];
11582794Sobrien      unsigned long value;
11682794Sobrien      const CGEN_INSN *insn = &insns[i];
11782794Sobrien
11882794Sobrien      if (! (* cd->dis_hash_p) (insn))
11982794Sobrien	continue;
12082794Sobrien
12182794Sobrien      /* We don't know whether the target uses the buffer or the base insn
12282794Sobrien	 to hash on, so set both up.  */
12382794Sobrien
12482794Sobrien      value = CGEN_INSN_BASE_VALUE (insn);
12582794Sobrien      bfd_put_bits ((bfd_vma) value,
12682794Sobrien		    buf,
12782794Sobrien		    CGEN_INSN_MASK_BITSIZE (insn),
12882794Sobrien		    big_p);
12982794Sobrien      hash = (* cd->dis_hash) (buf, value);
13082794Sobrien      add_insn_to_hash_chain (hentbuf, insn, htable, hash);
13182794Sobrien    }
13282794Sobrien
13382794Sobrien  return hentbuf;
13482794Sobrien}
13582794Sobrien
13682794Sobrien/* Subroutine of build_dis_hash_table to add INSNS to the hash table.
13782794Sobrien   This function is identical to hash_insn_array except the insns are
13882794Sobrien   in a list.  */
13982794Sobrien
14082794Sobrienstatic CGEN_INSN_LIST *
14182794Sobrienhash_insn_list (CGEN_CPU_DESC cd,
14282794Sobrien		const CGEN_INSN_LIST *insns,
14382794Sobrien		CGEN_INSN_LIST **htable,
14482794Sobrien		CGEN_INSN_LIST *hentbuf)
14582794Sobrien{
14682794Sobrien  int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
14782794Sobrien  const CGEN_INSN_LIST *ilist;
14882794Sobrien
14982794Sobrien  for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
15082794Sobrien    {
15182794Sobrien      unsigned int hash;
15282794Sobrien      char buf[4];
15382794Sobrien      unsigned long value;
15482794Sobrien
15582794Sobrien      if (! (* cd->dis_hash_p) (ilist->insn))
15682794Sobrien	continue;
15782794Sobrien
15882794Sobrien      /* We don't know whether the target uses the buffer or the base insn
15982794Sobrien	 to hash on, so set both up.  */
16082794Sobrien
16182794Sobrien      value = CGEN_INSN_BASE_VALUE (ilist->insn);
16282794Sobrien      bfd_put_bits((bfd_vma) value,
16382794Sobrien		   buf,
16482794Sobrien		   CGEN_INSN_MASK_BITSIZE (ilist->insn),
16582794Sobrien		   big_p);
16682794Sobrien      hash = (* cd->dis_hash) (buf, value);
16782794Sobrien      add_insn_to_hash_chain (hentbuf, ilist->insn, htable, hash);
16882794Sobrien    }
16982794Sobrien
17082794Sobrien  return hentbuf;
17182794Sobrien}
17282794Sobrien
17382794Sobrien/* Build the disassembler instruction hash table.  */
17482794Sobrien
17582794Sobrienstatic void
17682794Sobrienbuild_dis_hash_table (CGEN_CPU_DESC cd)
17782794Sobrien{
17882794Sobrien  int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
17982794Sobrien  CGEN_INSN_TABLE *insn_table = & cd->insn_table;
18082794Sobrien  CGEN_INSN_TABLE *macro_insn_table = & cd->macro_insn_table;
18182794Sobrien  unsigned int hash_size = cd->dis_hash_size;
18282794Sobrien  CGEN_INSN_LIST *hash_entry_buf;
18382794Sobrien  CGEN_INSN_LIST **dis_hash_table;
18482794Sobrien  CGEN_INSN_LIST *dis_hash_table_entries;
18582794Sobrien
18682794Sobrien  /* The space allocated for the hash table consists of two parts:
18782794Sobrien     the hash table and the hash lists.  */
18882794Sobrien
18982794Sobrien  dis_hash_table = (CGEN_INSN_LIST **)
19082794Sobrien    xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
19182794Sobrien  memset (dis_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
19282794Sobrien  dis_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
19382794Sobrien    xmalloc (count * sizeof (CGEN_INSN_LIST));
19482794Sobrien
19582794Sobrien  /* Add compiled in insns.
19682794Sobrien     Don't include the first one as it is a reserved entry.  */
19782794Sobrien  /* ??? It was the end of all hash chains, and also the special
19882794Sobrien     "invalid insn" marker.  May be able to do it differently now.  */
19982794Sobrien
20082794Sobrien  hash_entry_buf = hash_insn_array (cd,
20182794Sobrien				    insn_table->init_entries + 1,
20282794Sobrien				    insn_table->num_init_entries - 1,
20382794Sobrien				    insn_table->entry_size,
20482794Sobrien				    dis_hash_table, hash_entry_buf);
20582794Sobrien
20682794Sobrien  /* Add compiled in macro-insns.  */
20782794Sobrien
20882794Sobrien  hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
20982794Sobrien				    macro_insn_table->num_init_entries,
21082794Sobrien				    macro_insn_table->entry_size,
21182794Sobrien				    dis_hash_table, hash_entry_buf);
21282794Sobrien
21382794Sobrien  /* Add runtime added insns.
21482794Sobrien     Later added insns will be prefered over earlier ones.  */
21582794Sobrien
21682794Sobrien  hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
21782794Sobrien				   dis_hash_table, hash_entry_buf);
21882794Sobrien
21982794Sobrien  /* Add runtime added macro-insns.  */
22082794Sobrien
22182794Sobrien  hash_insn_list (cd, macro_insn_table->new_entries,
22282794Sobrien		  dis_hash_table, hash_entry_buf);
22382794Sobrien
22482794Sobrien  cd->dis_hash_table = dis_hash_table;
22582794Sobrien  cd->dis_hash_table_entries = dis_hash_table_entries;
22682794Sobrien}
22782794Sobrien
22882794Sobrien/* Return the first entry in the hash list for INSN.  */
22982794Sobrien
23082794SobrienCGEN_INSN_LIST *
23182794Sobriencgen_dis_lookup_insn (CGEN_CPU_DESC cd, const char * buf, CGEN_INSN_INT value)
23282794Sobrien{
23382794Sobrien  unsigned int hash;
23482794Sobrien
23582794Sobrien  if (cd->dis_hash_table == NULL)
23682794Sobrien    build_dis_hash_table (cd);
23782794Sobrien
23882794Sobrien  hash = (* cd->dis_hash) (buf, value);
23982794Sobrien
24082794Sobrien  return cd->dis_hash_table[hash];
24182794Sobrien}
24282794Sobrien