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