/* ia64-opc.c -- Functions to access the compacted opcode table Copyright 1999, 2000, 2001, 2003, 2005, 2007, 2009, 2012 Free Software Foundation, Inc. Written by Bob Manson of Cygnus Solutions, This file is part of the GNU opcodes library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. It is distributed in the hope that it 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include "libiberty.h" #include "ia64-asmtab.h" #include "ia64-asmtab.c" static void get_opc_prefix (const char **, char *); static short int find_string_ent (const char *); static short int find_main_ent (short int); static short int find_completer (short int, short int, const char *); static ia64_insn apply_completer (ia64_insn, int); static int extract_op_bits (int, int, int); static int extract_op (int, int *, unsigned int *); static int opcode_verify (ia64_insn, int, enum ia64_insn_type); static int locate_opcode_ent (ia64_insn, enum ia64_insn_type); static struct ia64_opcode *make_ia64_opcode (ia64_insn, const char *, int, int); static struct ia64_opcode *ia64_find_matching_opcode (const char *, short int); 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 (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 (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 (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 (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 (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 (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 (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 (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. Checks all matches and returns the one with the highest priority. */ static int locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type) { int currtest[41]; int bitpos[41]; int op_ptr[41]; int currstatenum = 0; short found_disent = -1; short found_priority = -1; 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] = {0}; 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; short priority = -1; 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; priority = ia64_dis_names[disent].priority; if (opcode_verify (opcode, place, type) && priority > found_priority) { break; } if (ia64_dis_names[disent].next_flag) { disent++; } else { disent = -1; } } if (disent >= 0) { found_disent = disent; found_priority = priority; } /* Try the next test in this state, regardless of whether a match was found. */ 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 found_disent; } } 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 (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 (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 (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 (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 (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 (struct ia64_opcode *ent) { free ((void *)ent->name); free (ent); } const struct ia64_dependency * ia64_find_dependency (int dep_index) { dep_index = DEP(dep_index); if (dep_index < 0 || dep_index >= (int) ARRAY_SIZE (dependencies)) return NULL; return &dependencies[dep_index]; }