diff options
author | Jim Wilson <wilson@tuliptree.org> | 2000-04-21 20:22:24 +0000 |
---|---|---|
committer | Jim Wilson <wilson@tuliptree.org> | 2000-04-21 20:22:24 +0000 |
commit | 800eeca487f145ccc5481a03bfff2b871a2fd361 (patch) | |
tree | cedc52859f0a66d17a78d5b7e772f8a6c3d3b693 /opcodes/ia64-opc.c | |
parent | c9637625e4ff16d9a9f3a203c5609cd5ada1eafa (diff) | |
download | gdb-800eeca487f145ccc5481a03bfff2b871a2fd361.zip gdb-800eeca487f145ccc5481a03bfff2b871a2fd361.tar.gz gdb-800eeca487f145ccc5481a03bfff2b871a2fd361.tar.bz2 |
IA-64 ELF support.
Diffstat (limited to 'opcodes/ia64-opc.c')
-rw-r--r-- | opcodes/ia64-opc.c | 741 |
1 files changed, 741 insertions, 0 deletions
diff --git a/opcodes/ia64-opc.c b/opcodes/ia64-opc.c new file mode 100644 index 0000000..a25dd66 --- /dev/null +++ b/opcodes/ia64-opc.c @@ -0,0 +1,741 @@ +/* ia64-opc.c -- Functions to access the compacted opcode table + Copyright (C) 1999 Free Software Foundation, Inc. + Written by Bob Manson of Cygnus Solutions, <manson@cygnus.com> + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "ansidecl.h" +#include "libiberty.h" +#include "sysdep.h" +#include "ia64-asmtab.h" +#include "ia64-asmtab.c" + +const struct ia64_templ_desc ia64_templ_desc[16] = + { + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */ + { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, + { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" }, + { 0, { 0, }, "-3-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */ + { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" }, + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */ + { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" }, + { 0, { 0, }, "-a-" }, + { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */ + { 0, { 0, }, "-d-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" }, + { 0, { 0, }, "-f-" }, + }; + + +/* Copy the prefix contained in *PTR (up to a '.' or a NUL) to DEST. + PTR will be adjusted to point to the start of the next portion + of the opcode, or at the NUL character. */ + +static void +get_opc_prefix (ptr, dest) + const char **ptr; + char *dest; +{ + char *c = strchr (*ptr, '.'); + if (c != NULL) + { + memcpy (dest, *ptr, c - *ptr); + dest[c - *ptr] = '\0'; + *ptr = c + 1; + } + else + { + int l = strlen (*ptr); + memcpy (dest, *ptr, l); + dest[l] = '\0'; + *ptr += l; + } +} + +/* Find the index of the entry in the string table corresponding to + STR; return -1 if one does not exist. */ + +static short +find_string_ent (str) + const char *str; +{ + short start = 0; + short end = sizeof (ia64_strings) / sizeof (const char *); + short i = (start + end) / 2; + + if (strcmp (str, ia64_strings[end - 1]) > 0) + { + return -1; + } + while (start <= end) + { + int c = strcmp (str, ia64_strings[i]); + if (c < 0) + { + end = i - 1; + } + else if (c == 0) + { + return i; + } + else + { + start = i + 1; + } + i = (start + end) / 2; + } + return -1; +} + +/* Find the opcode in the main opcode table whose name is STRINGINDEX, or + return -1 if one does not exist. */ + +static short +find_main_ent (nameindex) + short nameindex; +{ + short start = 0; + short end = sizeof (main_table) / sizeof (struct ia64_main_table); + short i = (start + end) / 2; + + if (nameindex < main_table[0].name_index + || nameindex > main_table[end - 1].name_index) + { + return -1; + } + while (start <= end) + { + if (nameindex < main_table[i].name_index) + { + end = i - 1; + } + else if (nameindex == main_table[i].name_index) + { + while (i > 0 && main_table[i - 1].name_index == nameindex) + { + i--; + } + return i; + } + else + { + start = i + 1; + } + i = (start + end) / 2; + } + return -1; +} + +/* Find the index of the entry in the completer table that is part of + MAIN_ENT (starting from PREV_COMPLETER) that matches NAME, or + return -1 if one does not exist. */ + +static short +find_completer (main_ent, prev_completer, name) + short main_ent; + short prev_completer; + const char *name; +{ + short name_index = find_string_ent (name); + + if (name_index < 0) + { + return -1; + } + + if (prev_completer == -1) + { + prev_completer = main_table[main_ent].completers; + } + else + { + prev_completer = completer_table[prev_completer].subentries; + } + + while (prev_completer != -1) + { + if (completer_table[prev_completer].name_index == name_index) + { + return prev_completer; + } + prev_completer = completer_table[prev_completer].alternative; + } + return -1; +} + +/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and + return the result. */ + +static ia64_insn +apply_completer (opcode, completer_index) + ia64_insn opcode; + int completer_index; +{ + ia64_insn mask = completer_table[completer_index].mask; + ia64_insn bits = completer_table[completer_index].bits; + int shiftamt = (completer_table[completer_index].offset & 63); + + mask = mask << shiftamt; + bits = bits << shiftamt; + opcode = (opcode & ~mask) | bits; + return opcode; +} + +/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in + the dis_table array, and return its value. (BITOFFSET is numbered + starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the + first byte in OP_POINTER.) */ + +static int +extract_op_bits (op_pointer, bitoffset, bits) + int op_pointer; + int bitoffset; + int bits; +{ + int res = 0; + + op_pointer += (bitoffset / 8); + + if (bitoffset % 8) + { + unsigned int op = dis_table[op_pointer++]; + int numb = 8 - (bitoffset % 8); + int mask = (1 << numb) - 1; + int bata = (bits < numb) ? bits : numb; + int delta = numb - bata; + + res = (res << bata) | ((op & mask) >> delta); + bitoffset += bata; + bits -= bata; + } + while (bits >= 8) + { + res = (res << 8) | (dis_table[op_pointer++] & 255); + bits -= 8; + } + if (bits > 0) + { + unsigned int op = (dis_table[op_pointer++] & 255); + res = (res << bits) | (op >> (8 - bits)); + } + return res; +} + +/* Examine the state machine entry at OP_POINTER in the dis_table + array, and extract its values into OPVAL and OP. The length of the + state entry in bits is returned. */ + +static int +extract_op (op_pointer, opval, op) + int op_pointer; + int *opval; + unsigned int *op; +{ + int oplen = 5; + + *op = dis_table[op_pointer]; + + if ((*op) & 0x40) + { + opval[0] = extract_op_bits (op_pointer, oplen, 5); + oplen += 5; + } + switch ((*op) & 0x30) + { + case 0x10: + { + opval[1] = extract_op_bits (op_pointer, oplen, 8); + oplen += 8; + opval[1] += op_pointer; + break; + } + case 0x20: + { + opval[1] = extract_op_bits (op_pointer, oplen, 16); + if (! (opval[1] & 32768)) + { + opval[1] += op_pointer; + } + oplen += 16; + break; + } + case 0x30: + { + oplen--; + opval[2] = extract_op_bits (op_pointer, oplen, 12); + oplen += 12; + opval[2] |= 32768; + break; + } + } + if (((*op) & 0x08) && (((*op) & 0x30) != 0x30)) + { + opval[2] = extract_op_bits (op_pointer, oplen, 16); + oplen += 16; + if (! (opval[2] & 32768)) + { + opval[2] += op_pointer; + } + } + return oplen; +} + +/* Returns a non-zero value if the opcode in the main_table list at + PLACE matches OPCODE and is of type TYPE. */ + +static int +opcode_verify (opcode, place, type) + ia64_insn opcode; + int place; + enum ia64_insn_type type; +{ + if (main_table[place].opcode_type != type) + { + return 0; + } + if (main_table[place].flags + & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT)) + { + const struct ia64_operand *o1, *o2; + ia64_insn f2, f3; + + if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3) + { + o1 = elf64_ia64_operands + IA64_OPND_F2; + o2 = elf64_ia64_operands + IA64_OPND_F3; + (*o1->extract) (o1, opcode, &f2); + (*o2->extract) (o2, opcode, &f3); + if (f2 != f3) + return 0; + } + else + { + ia64_insn len, count; + + /* length must equal 64-count: */ + o1 = elf64_ia64_operands + IA64_OPND_LEN6; + o2 = elf64_ia64_operands + main_table[place].operands[2]; + (*o1->extract) (o1, opcode, &len); + (*o2->extract) (o2, opcode, &count); + if (len != 64 - count) + return 0; + } + } + return 1; +} + +/* Find an instruction entry in the ia64_dis_names array that matches + opcode OPCODE and is of type TYPE. Returns either a positive index + into the array, or a negative value if an entry for OPCODE could + not be found. */ + +static int +locate_opcode_ent (opcode, type) + ia64_insn opcode; + enum ia64_insn_type type; +{ + int currtest[41]; + int bitpos[41]; + int op_ptr[41]; + int currstatenum = 0; + + currtest[currstatenum] = 0; + op_ptr[currstatenum] = 0; + bitpos[currstatenum] = 40; + + while (1) + { + int op_pointer = op_ptr[currstatenum]; + unsigned int op; + int currbitnum = bitpos[currstatenum]; + int oplen; + int opval[3]; + int next_op; + int currbit; + + oplen = extract_op (op_pointer, opval, &op); + + bitpos[currstatenum] = currbitnum; + + /* Skip opval[0] bits in the instruction. */ + if (op & 0x40) + { + currbitnum -= opval[0]; + } + + /* The value of the current bit being tested. */ + currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0; + next_op = -1; + + /* We always perform the tests specified in the current state in + a particular order, falling through to the next test if the + previous one failed. */ + switch (currtest[currstatenum]) + { + case 0: + currtest[currstatenum]++; + if (currbit == 0 && (op & 0x80)) + { + /* Check for a zero bit. If this test solely checks for + a zero bit, we can check for up to 8 consecutive zero + bits (the number to check is specified by the lower 3 + bits in the state code.) + + If the state instruction matches, we go to the very + next state instruction; otherwise, try the next test. */ + + if ((op & 0xf8) == 0x80) + { + int count = op & 0x7; + int x; + + for (x = 0; x <= count; x++) + { + int i = + opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0; + if (i) + { + break; + } + } + if (x > count) + { + next_op = op_pointer + ((oplen + 7) / 8); + currbitnum -= count; + break; + } + } + else if (! currbit) + { + next_op = op_pointer + ((oplen + 7) / 8); + break; + } + } + /* FALLTHROUGH */ + case 1: + /* If the bit in the instruction is one, go to the state + instruction specified by opval[1]. */ + currtest[currstatenum]++; + if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30)) + { + next_op = opval[1]; + break; + } + /* FALLTHROUGH */ + case 2: + /* Don't care. Skip the current bit and go to the state + instruction specified by opval[2]. + + An encoding of 0x30 is special; this means that a 12-bit + offset into the ia64_dis_names[] array is specified. */ + currtest[currstatenum]++; + if ((op & 0x08) || ((op & 0x30) == 0x30)) + { + next_op = opval[2]; + break; + } + } + + /* If bit 15 is set in the address of the next state, an offset + in the ia64_dis_names array was specified instead. We then + check to see if an entry in the list of opcodes matches the + opcode we were given; if so, we have succeeded. */ + + if ((next_op >= 0) && (next_op & 32768)) + { + short disent = next_op & 32767; + + if (next_op > 65535) + { + abort (); + } + + /* Run through the list of opcodes to check, trying to find + one that matches. */ + while (disent >= 0) + { + int place = ia64_dis_names[disent].insn_index; + + if (opcode_verify (opcode, place, type)) + { + break; + } + if (ia64_dis_names[disent].next_flag) + { + disent++; + } + else + { + disent = -1; + } + } + + if (disent >= 0) + { + return disent; + } + else + { + /* Failed to match; try the next test in this state. */ + next_op = -2; + } + } + + /* next_op == -1 is "back up to the previous state". + next_op == -2 is "stay in this state and try the next test". + Otherwise, transition to the state indicated by next_op. */ + + if (next_op == -1) + { + currstatenum--; + if (currstatenum < 0) + { + return -1; + } + } + else if (next_op >= 0) + { + currstatenum++; + bitpos[currstatenum] = currbitnum - 1; + op_ptr[currstatenum] = next_op; + currtest[currstatenum] = 0; + } + } +} + +/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */ + +static struct ia64_opcode * +make_ia64_opcode (opcode, name, place, depind) + ia64_insn opcode; + const char *name; + int place; + int depind; +{ + struct ia64_opcode *res = + (struct ia64_opcode *) xmalloc (sizeof (struct ia64_opcode)); + res->name = xstrdup (name); + res->type = main_table[place].opcode_type; + res->num_outputs = main_table[place].num_outputs; + res->opcode = opcode; + res->mask = main_table[place].mask; + res->operands[0] = main_table[place].operands[0]; + res->operands[1] = main_table[place].operands[1]; + res->operands[2] = main_table[place].operands[2]; + res->operands[3] = main_table[place].operands[3]; + res->operands[4] = main_table[place].operands[4]; + res->flags = main_table[place].flags; + res->ent_index = place; + res->dependencies = &op_dependencies[depind]; + return res; +} + +/* Determine the ia64_opcode entry for the opcode specified by INSN + and TYPE. If a valid entry is not found, return NULL. */ +struct ia64_opcode * +ia64_dis_opcode (insn, type) + ia64_insn insn; + enum ia64_insn_type type; +{ + int disent = locate_opcode_ent (insn, type); + + if (disent < 0) + { + return NULL; + } + else + { + unsigned int cb = ia64_dis_names[disent].completer_index; + static char name[128]; + int place = ia64_dis_names[disent].insn_index; + int ci = main_table[place].completers; + ia64_insn tinsn = main_table[place].opcode; + + strcpy (name, ia64_strings [main_table[place].name_index]); + + while (cb) + { + if (cb & 1) + { + int cname = completer_table[ci].name_index; + + tinsn = apply_completer (tinsn, ci); + + if (ia64_strings[cname][0] != '\0') + { + strcat (name, "."); + strcat (name, ia64_strings[cname]); + } + if (cb != 1) + { + ci = completer_table[ci].subentries; + } + } + else + { + ci = completer_table[ci].alternative; + } + if (ci < 0) + { + abort (); + } + cb = cb >> 1; + } + if (tinsn != (insn & main_table[place].mask)) + { + abort (); + } + return make_ia64_opcode (insn, name, place, + completer_table[ci].dependencies); + } +} + +/* Search the main_opcode table starting from PLACE for an opcode that + matches NAME. Return NULL if one is not found. */ + +static struct ia64_opcode * +ia64_find_matching_opcode (name, place) + const char *name; + short place; +{ + char op[129]; + const char *suffix; + short name_index; + + if (strlen (name) > 128) + { + return NULL; + } + suffix = name; + get_opc_prefix (&suffix, op); + name_index = find_string_ent (op); + if (name_index < 0) + { + return NULL; + } + + while (main_table[place].name_index == name_index) + { + const char *curr_suffix = suffix; + ia64_insn curr_insn = main_table[place].opcode; + short completer = -1; + + do { + if (suffix[0] == '\0') + { + completer = find_completer (place, completer, suffix); + } + else + { + get_opc_prefix (&curr_suffix, op); + completer = find_completer (place, completer, op); + } + if (completer != -1) + { + curr_insn = apply_completer (curr_insn, completer); + } + } while (completer != -1 && curr_suffix[0] != '\0'); + + if (completer != -1 && curr_suffix[0] == '\0' + && completer_table[completer].terminal_completer) + { + int depind = completer_table[completer].dependencies; + return make_ia64_opcode (curr_insn, name, place, depind); + } + else + { + place++; + } + } + return NULL; +} + +/* Find the next opcode after PREV_ENT that matches PREV_ENT, or return NULL + if one does not exist. + + It is the caller's responsibility to invoke ia64_free_opcode () to + release any resources used by the returned entry. */ + +struct ia64_opcode * +ia64_find_next_opcode (prev_ent) + struct ia64_opcode *prev_ent; +{ + return ia64_find_matching_opcode (prev_ent->name, + prev_ent->ent_index + 1); +} + +/* Find the first opcode that matches NAME, or return NULL if it does + not exist. + + It is the caller's responsibility to invoke ia64_free_opcode () to + release any resources used by the returned entry. */ + +struct ia64_opcode * +ia64_find_opcode (name) + const char *name; +{ + char op[129]; + const char *suffix; + short place; + short name_index; + + if (strlen (name) > 128) + { + return NULL; + } + suffix = name; + get_opc_prefix (&suffix, op); + name_index = find_string_ent (op); + if (name_index < 0) + { + return NULL; + } + + place = find_main_ent (name_index); + + if (place < 0) + { + return NULL; + } + return ia64_find_matching_opcode (name, place); +} + +/* Free any resources used by ENT. */ +void +ia64_free_opcode (ent) + struct ia64_opcode *ent; +{ + free ((void *)ent->name); + free (ent); +} + +const struct ia64_dependency * +ia64_find_dependency (index) + int index; +{ + index = DEP(index); + + if (index < 0 || index >= sizeof(dependencies) / sizeof(dependencies[0])) + return NULL; + + return &dependencies[index]; +} |