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