133965Sjdp/* CGEN generic disassembler support code.
233965Sjdp
3218822Sdim   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
485815Sobrien   Free Software Foundation, Inc.
533965Sjdp
638889Sjdp   This file is part of the GNU Binutils and GDB, the GNU debugger.
733965Sjdp
838889Sjdp   This program is free software; you can redistribute it and/or modify
938889Sjdp   it under the terms of the GNU General Public License as published by
1038889Sjdp   the Free Software Foundation; either version 2, or (at your option)
1138889Sjdp   any later version.
1233965Sjdp
1338889Sjdp   This program is distributed in the hope that it will be useful,
1438889Sjdp   but WITHOUT ANY WARRANTY; without even the implied warranty of
1538889Sjdp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1638889Sjdp   GNU General Public License for more details.
1733965Sjdp
1838889Sjdp   You should have received a copy of the GNU General Public License along
1938889Sjdp   with this program; if not, write to the Free Software Foundation, Inc.,
20218822Sdim   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2133965Sjdp
2233965Sjdp#include "sysdep.h"
2333965Sjdp#include <stdio.h>
2433965Sjdp#include "ansidecl.h"
2533965Sjdp#include "libiberty.h"
2633965Sjdp#include "bfd.h"
2738889Sjdp#include "symcat.h"
2833965Sjdp#include "opcode/cgen.h"
2933965Sjdp
30130561Sobrienstatic CGEN_INSN_LIST *  hash_insn_array        (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
31130561Sobrienstatic CGEN_INSN_LIST *  hash_insn_list         (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *);
32130561Sobrienstatic void              build_dis_hash_table   (CGEN_CPU_DESC);
33130561Sobrienstatic int		 count_decodable_bits   (const CGEN_INSN *);
34130561Sobrienstatic void		 add_insn_to_hash_chain (CGEN_INSN_LIST *,
35130561Sobrien						 const CGEN_INSN *,
36130561Sobrien						 CGEN_INSN_LIST **,
37130561Sobrien						 unsigned int);
3889857Sobrien
3989857Sobrien/* Return the number of decodable bits in this insn.  */
4089857Sobrienstatic int
41130561Sobriencount_decodable_bits (const CGEN_INSN *insn)
4289857Sobrien{
4389857Sobrien  unsigned mask = CGEN_INSN_BASE_MASK (insn);
4489857Sobrien  int bits = 0;
4589857Sobrien  int m;
4689857Sobrien  for (m = 1; m != 0; m <<= 1)
4789857Sobrien    {
4889857Sobrien      if (mask & m)
4989857Sobrien	++bits;
5089857Sobrien    }
5189857Sobrien  return bits;
5289857Sobrien}
5389857Sobrien
5489857Sobrien/* Add an instruction to the hash chain.  */
5589857Sobrienstatic void
56130561Sobrienadd_insn_to_hash_chain (CGEN_INSN_LIST *hentbuf,
57130561Sobrien			const CGEN_INSN *insn,
58130561Sobrien			CGEN_INSN_LIST **htable,
59130561Sobrien			unsigned int hash)
6089857Sobrien{
6189857Sobrien  CGEN_INSN_LIST *current_buf;
6289857Sobrien  CGEN_INSN_LIST *previous_buf;
6389857Sobrien  int insn_decodable_bits;
6489857Sobrien
6589857Sobrien  /* Add insns sorted by the number of decodable bits, in decreasing order.
6689857Sobrien     This ensures that any insn which is a special case of another will be
6789857Sobrien     checked first.  */
6889857Sobrien  insn_decodable_bits = count_decodable_bits (insn);
6989857Sobrien  previous_buf = NULL;
7089857Sobrien  for (current_buf = htable[hash]; current_buf != NULL;
7189857Sobrien       current_buf = current_buf->next)
7289857Sobrien    {
7389857Sobrien      int current_decodable_bits = count_decodable_bits (current_buf->insn);
7489857Sobrien      if (insn_decodable_bits >= current_decodable_bits)
7589857Sobrien	break;
7689857Sobrien      previous_buf = current_buf;
7789857Sobrien    }
7889857Sobrien
7989857Sobrien  /* Now insert the new insn.  */
8089857Sobrien  hentbuf->insn = insn;
8189857Sobrien  hentbuf->next = current_buf;
8289857Sobrien  if (previous_buf == NULL)
8389857Sobrien    htable[hash] = hentbuf;
8489857Sobrien  else
8589857Sobrien    previous_buf->next = hentbuf;
8689857Sobrien}
8789857Sobrien
8860484Sobrien/* Subroutine of build_dis_hash_table to add INSNS to the hash table.
8933965Sjdp
9060484Sobrien   COUNT is the number of elements in INSNS.
9160484Sobrien   ENTSIZE is sizeof (CGEN_IBASE) for the target.
9260484Sobrien   ??? No longer used but leave in for now.
9360484Sobrien   HTABLE points to the hash table.
9460484Sobrien   HENTBUF is a pointer to sufficiently large buffer of hash entries.
9560484Sobrien   The result is a pointer to the next entry to use.
9633965Sjdp
9760484Sobrien   The table is scanned backwards as additions are made to the front of the
9860484Sobrien   list and we want earlier ones to be prefered.  */
9933965Sjdp
10060484Sobrienstatic CGEN_INSN_LIST *
101130561Sobrienhash_insn_array (CGEN_CPU_DESC cd,
102130561Sobrien		 const CGEN_INSN * insns,
103130561Sobrien		 int count,
104130561Sobrien		 int entsize ATTRIBUTE_UNUSED,
105130561Sobrien		 CGEN_INSN_LIST ** htable,
106130561Sobrien		 CGEN_INSN_LIST * hentbuf)
10733965Sjdp{
10860484Sobrien  int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
10960484Sobrien  int i;
11033965Sjdp
11160484Sobrien  for (i = count - 1; i >= 0; --i, ++hentbuf)
11260484Sobrien    {
11360484Sobrien      unsigned int hash;
11460484Sobrien      char buf [4];
11560484Sobrien      unsigned long value;
11660484Sobrien      const CGEN_INSN *insn = &insns[i];
11733965Sjdp
11860484Sobrien      if (! (* cd->dis_hash_p) (insn))
11960484Sobrien	continue;
12033965Sjdp
12133965Sjdp      /* We don't know whether the target uses the buffer or the base insn
12233965Sjdp	 to hash on, so set both up.  */
12360484Sobrien
12460484Sobrien      value = CGEN_INSN_BASE_VALUE (insn);
12585815Sobrien      bfd_put_bits ((bfd_vma) value,
12685815Sobrien		    buf,
12785815Sobrien		    CGEN_INSN_MASK_BITSIZE (insn),
12885815Sobrien		    big_p);
12960484Sobrien      hash = (* cd->dis_hash) (buf, value);
13089857Sobrien      add_insn_to_hash_chain (hentbuf, insn, htable, hash);
13160484Sobrien    }
13238889Sjdp
13360484Sobrien  return hentbuf;
13460484Sobrien}
13538889Sjdp
13660484Sobrien/* Subroutine of build_dis_hash_table to add INSNS to the hash table.
13760484Sobrien   This function is identical to hash_insn_array except the insns are
13860484Sobrien   in a list.  */
13933965Sjdp
14060484Sobrienstatic CGEN_INSN_LIST *
141130561Sobrienhash_insn_list (CGEN_CPU_DESC cd,
142130561Sobrien		const CGEN_INSN_LIST *insns,
143130561Sobrien		CGEN_INSN_LIST **htable,
144130561Sobrien		CGEN_INSN_LIST *hentbuf)
14560484Sobrien{
14660484Sobrien  int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
14760484Sobrien  const CGEN_INSN_LIST *ilist;
14860484Sobrien
14960484Sobrien  for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
15033965Sjdp    {
15160484Sobrien      unsigned int hash;
15260484Sobrien      char buf[4];
15360484Sobrien      unsigned long value;
15460484Sobrien
15560484Sobrien      if (! (* cd->dis_hash_p) (ilist->insn))
15660484Sobrien	continue;
15760484Sobrien
15833965Sjdp      /* We don't know whether the target uses the buffer or the base insn
15933965Sjdp	 to hash on, so set both up.  */
16060484Sobrien
16160484Sobrien      value = CGEN_INSN_BASE_VALUE (ilist->insn);
16285815Sobrien      bfd_put_bits((bfd_vma) value,
16385815Sobrien		   buf,
16485815Sobrien		   CGEN_INSN_MASK_BITSIZE (ilist->insn),
16585815Sobrien		   big_p);
16660484Sobrien      hash = (* cd->dis_hash) (buf, value);
16789857Sobrien      add_insn_to_hash_chain (hentbuf, ilist->insn, htable, hash);
16860484Sobrien    }
16938889Sjdp
17060484Sobrien  return hentbuf;
17160484Sobrien}
17238889Sjdp
17360484Sobrien/* Build the disassembler instruction hash table.  */
17460484Sobrien
17560484Sobrienstatic void
176130561Sobrienbuild_dis_hash_table (CGEN_CPU_DESC cd)
17760484Sobrien{
17860484Sobrien  int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
17960484Sobrien  CGEN_INSN_TABLE *insn_table = & cd->insn_table;
18060484Sobrien  CGEN_INSN_TABLE *macro_insn_table = & cd->macro_insn_table;
18160484Sobrien  unsigned int hash_size = cd->dis_hash_size;
18260484Sobrien  CGEN_INSN_LIST *hash_entry_buf;
18360484Sobrien  CGEN_INSN_LIST **dis_hash_table;
18460484Sobrien  CGEN_INSN_LIST *dis_hash_table_entries;
18560484Sobrien
18660484Sobrien  /* The space allocated for the hash table consists of two parts:
18760484Sobrien     the hash table and the hash lists.  */
18860484Sobrien
18960484Sobrien  dis_hash_table = (CGEN_INSN_LIST **)
19060484Sobrien    xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
19160484Sobrien  memset (dis_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
19260484Sobrien  dis_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
19360484Sobrien    xmalloc (count * sizeof (CGEN_INSN_LIST));
19460484Sobrien
19560484Sobrien  /* Add compiled in insns.
19660484Sobrien     Don't include the first one as it is a reserved entry.  */
19760484Sobrien  /* ??? It was the end of all hash chains, and also the special
19860484Sobrien     "invalid insn" marker.  May be able to do it differently now.  */
19960484Sobrien
20060484Sobrien  hash_entry_buf = hash_insn_array (cd,
20160484Sobrien				    insn_table->init_entries + 1,
20260484Sobrien				    insn_table->num_init_entries - 1,
20360484Sobrien				    insn_table->entry_size,
20460484Sobrien				    dis_hash_table, hash_entry_buf);
20560484Sobrien
20660484Sobrien  /* Add compiled in macro-insns.  */
20760484Sobrien
20860484Sobrien  hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
20960484Sobrien				    macro_insn_table->num_init_entries,
21060484Sobrien				    macro_insn_table->entry_size,
21160484Sobrien				    dis_hash_table, hash_entry_buf);
21260484Sobrien
21360484Sobrien  /* Add runtime added insns.
21460484Sobrien     Later added insns will be prefered over earlier ones.  */
21560484Sobrien
21660484Sobrien  hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
21760484Sobrien				   dis_hash_table, hash_entry_buf);
21860484Sobrien
21960484Sobrien  /* Add runtime added macro-insns.  */
22060484Sobrien
22160484Sobrien  hash_insn_list (cd, macro_insn_table->new_entries,
22260484Sobrien		  dis_hash_table, hash_entry_buf);
22360484Sobrien
22460484Sobrien  cd->dis_hash_table = dis_hash_table;
22560484Sobrien  cd->dis_hash_table_entries = dis_hash_table_entries;
22633965Sjdp}
22733965Sjdp
22833965Sjdp/* Return the first entry in the hash list for INSN.  */
22933965Sjdp
23033965SjdpCGEN_INSN_LIST *
231130561Sobriencgen_dis_lookup_insn (CGEN_CPU_DESC cd, const char * buf, CGEN_INSN_INT value)
23233965Sjdp{
23333965Sjdp  unsigned int hash;
23433965Sjdp
23560484Sobrien  if (cd->dis_hash_table == NULL)
23660484Sobrien    build_dis_hash_table (cd);
23733965Sjdp
23860484Sobrien  hash = (* cd->dis_hash) (buf, value);
23938889Sjdp
24060484Sobrien  return cd->dis_hash_table[hash];
24133965Sjdp}
242