| /* $Id$ |
| * |
| * Generate Arch Parser (GAP): generates ARCHparse.c from ARCHparse.gap. |
| * |
| * Copyright (C) 2006 Peter Johnson |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include "tools/gap/perfect.h" |
| #include "libyasm/compat-queue.h" |
| #include "libyasm/coretype.h" |
| #include "libyasm/errwarn.h" |
| |
| typedef STAILQ_HEAD(slist, sval) slist; |
| typedef struct sval { |
| STAILQ_ENTRY(sval) link; |
| char *str; |
| } sval; |
| |
| typedef STAILQ_HEAD(dir_list, dir) dir_list; |
| typedef struct dir { |
| STAILQ_ENTRY(dir) link; |
| char *name; |
| const char *func; |
| slist args; |
| } dir; |
| |
| typedef STAILQ_HEAD(dir_byp_list, dir_byp) dir_byp_list; |
| typedef struct dir_byp { |
| STAILQ_ENTRY(dir_byp) link; |
| /*@null@*/ char *parser; |
| dir_list dirs; |
| } dir_byp; |
| |
| typedef enum { |
| ARCH = 0, |
| PARSERS, |
| INSN, |
| CPU, |
| CPU_ALIAS, |
| CPU_FEATURE, |
| TARGETMOD, |
| PREFIX, |
| REG, |
| REGGROUP, |
| SEGREG, |
| NUM_DIRS |
| } dir_type; |
| |
| typedef struct { |
| void (*parse_insn) (void); /* arch-specific parse_insn */ |
| int multi_parser[NUM_DIRS]; /* whether it has an initial parser field */ |
| } arch_handler; |
| |
| static void x86_parse_insn(void); |
| static const arch_handler arch_x86 = { |
| x86_parse_insn, |
| {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0} |
| }; |
| |
| static struct { |
| const char *name; |
| const arch_handler *arch; |
| } archs[] = { |
| {"x86", &arch_x86}, |
| }; |
| |
| static char line[1024]; |
| static unsigned int cur_line = 0, next_line = 1; |
| static int errors = 0; |
| static const arch_handler *arch = NULL; |
| |
| /* Lists of directives, keyed by parser name */ |
| static dir_byp_list insnprefix_byp; |
| static dir_byp_list cpu_byp; |
| static dir_byp_list regtmod_byp; |
| |
| static void |
| report_error(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| fprintf(stderr, "%u: ", cur_line); |
| va_start(ap, fmt); |
| vfprintf(stderr, fmt, ap); |
| va_end(ap); |
| fputc('\n', stderr); |
| errors++; |
| } |
| |
| void |
| yasm__fatal(const char *message, ...) |
| { |
| abort(); |
| } |
| |
| static void |
| dup_slist(slist *out, slist *in) |
| { |
| sval *sv; |
| |
| STAILQ_INIT(out); |
| STAILQ_FOREACH(sv, in, link) { |
| sval *nsv = yasm_xmalloc(sizeof(sval)); |
| nsv->str = yasm__xstrdup(sv->str); |
| STAILQ_INSERT_TAIL(out, nsv, link); |
| } |
| } |
| |
| static dir * |
| dup_dir(dir *in) |
| { |
| dir *out = yasm_xmalloc(sizeof(dir)); |
| out->name = yasm__xstrdup(in->name); |
| out->func = in->func; |
| dup_slist(&out->args, &in->args); |
| return out; |
| } |
| |
| static dir_list * |
| get_dirs(dir_byp_list *byp, /*@null@*/ const char *parser) |
| { |
| dir_list *found = NULL; |
| dir_byp *db; |
| |
| if (STAILQ_EMPTY(byp)) { |
| report_error("PARSERS not yet specified"); |
| return NULL; |
| } |
| |
| STAILQ_FOREACH(db, byp, link) { |
| if ((!parser && !db->parser) || |
| (parser && db->parser && strcmp(parser, db->parser) == 0)) { |
| found = &db->dirs; |
| break; |
| } |
| } |
| |
| return found; |
| } |
| |
| /* Add a keyword/data to a slist of slist keyed by parser name. |
| * Returns nonzero on error. |
| */ |
| static int |
| add_dir(dir_byp_list *byp, /*@null@*/ const char *parser, dir *d) |
| { |
| dir_list *found = get_dirs(byp, parser); |
| |
| if (found) { |
| STAILQ_INSERT_TAIL(found, d, link); |
| return 0; |
| } else if (!parser) { |
| /* Add separately to all */ |
| dir_byp *db; |
| int first = 1; |
| STAILQ_FOREACH(db, byp, link) { |
| if (!first) |
| d = dup_dir(d); |
| first = 0; |
| STAILQ_INSERT_TAIL(&db->dirs, d, link); |
| } |
| return 0; |
| } else { |
| report_error("parser not found"); |
| return 1; |
| } |
| } |
| |
| static char * |
| check_parser(dir_type type) |
| { |
| char *parser = NULL; |
| |
| if (arch->multi_parser[type]) { |
| parser = strtok(NULL, " \t\n"); |
| if (strcmp(parser, "-") == 0) |
| parser = NULL; |
| } |
| |
| return parser; |
| } |
| |
| static void |
| parse_args(slist *args) |
| { |
| char *tok; |
| sval *sv; |
| |
| STAILQ_INIT(args); |
| |
| tok = strtok(NULL, " \t\n"); |
| if (!tok) { |
| report_error("no args"); |
| return; |
| } |
| |
| while (tok) { |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup(tok); |
| STAILQ_INSERT_TAIL(args, sv, link); |
| tok = strtok(NULL, " \t\n"); |
| } |
| } |
| |
| static dir * |
| parse_generic(dir_type type, const char *func, dir_byp_list *byp) |
| { |
| char *parser = check_parser(type); |
| char *name = strtok(NULL, " \t\n"); |
| dir *d = yasm_xmalloc(sizeof(dir)); |
| |
| d->name = yasm__xstrdup(name); |
| d->func = func; |
| parse_args(&d->args); |
| |
| add_dir(byp, parser, d); |
| return d; |
| } |
| |
| static void |
| parse_arch(void) |
| { |
| size_t i; |
| int found = 0; |
| char *tok = strtok(NULL, " \t\n"); |
| |
| if (!tok) { |
| report_error("ARCH requires an operand"); |
| return; |
| } |
| for (i=0; i<sizeof(archs)/sizeof(archs[0]); i++) { |
| if (strcmp(archs[i].name, tok) == 0) { |
| found = 1; |
| break; |
| } |
| } |
| if (!found) { |
| report_error("unrecognized ARCH"); |
| return; |
| } |
| |
| arch = archs[i].arch; |
| } |
| |
| static void |
| parse_parsers(void) |
| { |
| dir_byp *db; |
| char *tok; |
| |
| if (!arch) { |
| report_error("ARCH not specified before PARSERS"); |
| return; |
| } |
| |
| tok = strtok(NULL, " \t\n"); |
| if (!tok) { |
| report_error("no PARSERS parameter"); |
| return; |
| } |
| |
| while (tok) { |
| /* Insert into each slist of slist if broken out by parser */ |
| if (arch->multi_parser[INSN] || arch->multi_parser[PREFIX]) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = yasm__xstrdup(tok); |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(&insnprefix_byp, db, link); |
| } |
| if (arch->multi_parser[CPU] || arch->multi_parser[CPU_ALIAS] || |
| arch->multi_parser[CPU_FEATURE]) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = yasm__xstrdup(tok); |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(&cpu_byp, db, link); |
| } |
| if (arch->multi_parser[TARGETMOD] || arch->multi_parser[REG] || |
| arch->multi_parser[REGGROUP] || arch->multi_parser[SEGREG]) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = yasm__xstrdup(tok); |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(®tmod_byp, db, link); |
| } |
| tok = strtok(NULL, " \t\n"); |
| } |
| |
| /* Add NULL (global) versions if not already created */ |
| if (STAILQ_EMPTY(&insnprefix_byp)) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = NULL; |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(&insnprefix_byp, db, link); |
| } |
| if (STAILQ_EMPTY(&cpu_byp)) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = NULL; |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(&cpu_byp, db, link); |
| } |
| if (STAILQ_EMPTY(®tmod_byp)) { |
| db = yasm_xmalloc(sizeof(dir_byp)); |
| db->parser = NULL; |
| STAILQ_INIT(&db->dirs); |
| |
| STAILQ_INSERT_TAIL(®tmod_byp, db, link); |
| } |
| } |
| |
| static void |
| x86_parse_insn(void) |
| { |
| char *parser = check_parser(INSN); |
| char *bname = strtok(NULL, " \t\n"); |
| char *suffix = strtok(NULL, " \t\n"); |
| dir *d; |
| slist args; |
| sval *sv; |
| |
| if (!suffix) { |
| report_error("INSN requires suffix"); |
| return; |
| } |
| |
| /* save the remainder of args */ |
| parse_args(&args); |
| |
| if (suffix[0] != '"') { |
| /* Just one instruction to generate */ |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup(suffix); |
| STAILQ_INSERT_HEAD(&args, sv, link); |
| |
| d = yasm_xmalloc(sizeof(dir)); |
| d->name = yasm__xstrdup(bname); |
| d->func = "INSN"; |
| d->args = args; |
| add_dir(&insnprefix_byp, parser, d); |
| } else { |
| /* Need to generate with suffixes for gas */ |
| char *p; |
| char sufstr[6]; |
| size_t bnamelen = strlen(bname); |
| |
| strcpy(sufstr, "SUF_X"); |
| |
| for (p = &suffix[1]; *p != '"'; p++) { |
| sufstr[4] = toupper(*p); |
| |
| d = yasm_xmalloc(sizeof(dir)); |
| |
| d->name = yasm_xmalloc(bnamelen+2); |
| strcpy(d->name, bname); |
| d->name[bnamelen] = tolower(*p); |
| d->name[bnamelen+1] = '\0'; |
| |
| d->func = "INSN"; |
| dup_slist(&d->args, &args); |
| |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup(sufstr); |
| STAILQ_INSERT_HEAD(&d->args, sv, link); |
| |
| add_dir(&insnprefix_byp, "gas", d); |
| } |
| |
| /* And finally the version sans suffix */ |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup("NONE"); |
| STAILQ_INSERT_HEAD(&args, sv, link); |
| |
| d = yasm_xmalloc(sizeof(dir)); |
| d->name = yasm__xstrdup(bname); |
| d->func = "INSN"; |
| d->args = args; |
| add_dir(&insnprefix_byp, parser, d); |
| } |
| } |
| |
| static void |
| parse_insn(void) |
| { |
| if (!arch) { |
| report_error("ARCH not defined prior to INSN"); |
| return; |
| } |
| arch->parse_insn(); |
| } |
| |
| static void |
| parse_cpu(void) |
| { |
| dir *d = parse_generic(CPU, "CPU", &cpu_byp); |
| sval *sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup("CPU_MODE_VERBATIM"); |
| STAILQ_INSERT_TAIL(&d->args, sv, link); |
| } |
| |
| static void |
| parse_cpu_alias(void) |
| { |
| char *parser = check_parser(CPU_ALIAS); |
| char *name = strtok(NULL, " \t\n"); |
| char *alias = strtok(NULL, " \t\n"); |
| dir_list *dirs = get_dirs(&cpu_byp, parser); |
| dir *aliasd, *d; |
| |
| if (!alias) { |
| report_error("CPU_ALIAS requires an operand"); |
| return; |
| } |
| |
| STAILQ_FOREACH(aliasd, dirs, link) { |
| if (strcmp(aliasd->name, alias) == 0) |
| break; |
| } |
| if (!aliasd) { |
| report_error("could not find `%s'", alias); |
| return; |
| } |
| |
| d = yasm_xmalloc(sizeof(dir)); |
| d->name = yasm__xstrdup(name); |
| d->func = "CPU"; |
| dup_slist(&d->args, &aliasd->args); |
| |
| add_dir(&cpu_byp, parser, d); |
| } |
| |
| static void |
| parse_cpu_feature(void) |
| { |
| char *parser = check_parser(CPU_FEATURE); |
| char *name = strtok(NULL, " \t\n"); |
| dir *name_dir = yasm_xmalloc(sizeof(dir)); |
| dir *noname_dir = yasm_xmalloc(sizeof(dir)); |
| sval *sv; |
| |
| name_dir->name = yasm__xstrdup(name); |
| name_dir->func = "CPU_FEATURE"; |
| parse_args(&name_dir->args); |
| |
| noname_dir->name = yasm_xmalloc(strlen(name)+3); |
| strcpy(noname_dir->name, "no"); |
| strcat(noname_dir->name, name); |
| noname_dir->func = name_dir->func; |
| dup_slist(&noname_dir->args, &name_dir->args); |
| |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup("CPU_MODE_SET"); |
| STAILQ_INSERT_TAIL(&name_dir->args, sv, link); |
| |
| sv = yasm_xmalloc(sizeof(sval)); |
| sv->str = yasm__xstrdup("CPU_MODE_CLEAR"); |
| STAILQ_INSERT_TAIL(&noname_dir->args, sv, link); |
| |
| add_dir(&cpu_byp, parser, name_dir); |
| add_dir(&cpu_byp, parser, noname_dir); |
| } |
| |
| static void |
| parse_targetmod(void) |
| { |
| parse_generic(TARGETMOD, "TARGETMOD", ®tmod_byp); |
| } |
| |
| static void |
| parse_prefix(void) |
| { |
| parse_generic(PREFIX, "PREFIX", &insnprefix_byp); |
| } |
| |
| static void |
| parse_reg(void) |
| { |
| parse_generic(REG, "REG", ®tmod_byp); |
| } |
| |
| static void |
| parse_reggroup(void) |
| { |
| parse_generic(REGGROUP, "REGGROUP", ®tmod_byp); |
| } |
| |
| static void |
| parse_segreg(void) |
| { |
| parse_generic(SEGREG, "SEGREG", ®tmod_byp); |
| } |
| |
| /* make the c output for the perfect hash tab array */ |
| static void |
| make_c_tab( |
| FILE *f, |
| const char *which, |
| const char *parser, |
| bstuff *tab, /* table indexed by b */ |
| ub4 smax, /* range of scramble[] */ |
| ub4 blen, /* b in 0..blen-1, power of 2 */ |
| ub4 *scramble) /* used in final hash */ |
| { |
| ub4 i; |
| /* table for the mapping for the perfect hash */ |
| if (blen >= USE_SCRAMBLE) { |
| /* A way to make the 1-byte values in tab bigger */ |
| if (smax > UB2MAXVAL+1) { |
| fprintf(f, "static const unsigned long %s_", which); |
| if (parser) |
| fprintf(f, "%s_", parser); |
| fprintf(f, "scramble[] = {\n"); |
| for (i=0; i<=UB1MAXVAL; i+=4) |
| fprintf(f, "0x%.8lx, 0x%.8lx, 0x%.8lx, 0x%.8lx,\n", |
| scramble[i+0], scramble[i+1], scramble[i+2], scramble[i+3]); |
| } else { |
| fprintf(f, "static const unsigned short %s_", which); |
| if (parser) |
| fprintf(f, "%s_", parser); |
| fprintf(f, "scramble[] = {\n"); |
| for (i=0; i<=UB1MAXVAL; i+=8) |
| fprintf(f, |
| "0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx,\n", |
| scramble[i+0], scramble[i+1], scramble[i+2], scramble[i+3], |
| scramble[i+4], scramble[i+5], scramble[i+6], scramble[i+7]); |
| } |
| fprintf(f, "};\n"); |
| fprintf(f, "\n"); |
| } |
| |
| if (blen > 0) { |
| /* small adjustments to _a_ to make values distinct */ |
| if (smax <= UB1MAXVAL+1 || blen >= USE_SCRAMBLE) |
| fprintf(f, "static const unsigned char %s_", which); |
| else |
| fprintf(f, "static const unsigned short %s_", which); |
| if (parser) |
| fprintf(f, "%s_", parser); |
| fprintf(f, "tab[] = {\n"); |
| |
| if (blen < 16) { |
| for (i=0; i<blen; ++i) |
| fprintf(f, "%3ld,", scramble[tab[i].val_b]); |
| } else if (blen <= 1024) { |
| for (i=0; i<blen; i+=16) |
| fprintf(f, "%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n", |
| scramble[tab[i+0].val_b], scramble[tab[i+1].val_b], |
| scramble[tab[i+2].val_b], scramble[tab[i+3].val_b], |
| scramble[tab[i+4].val_b], scramble[tab[i+5].val_b], |
| scramble[tab[i+6].val_b], scramble[tab[i+7].val_b], |
| scramble[tab[i+8].val_b], scramble[tab[i+9].val_b], |
| scramble[tab[i+10].val_b], scramble[tab[i+11].val_b], |
| scramble[tab[i+12].val_b], scramble[tab[i+13].val_b], |
| scramble[tab[i+14].val_b], scramble[tab[i+15].val_b]); |
| } else if (blen < USE_SCRAMBLE) { |
| for (i=0; i<blen; i+=8) |
| fprintf(f, "%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n", |
| scramble[tab[i+0].val_b], scramble[tab[i+1].val_b], |
| scramble[tab[i+2].val_b], scramble[tab[i+3].val_b], |
| scramble[tab[i+4].val_b], scramble[tab[i+5].val_b], |
| scramble[tab[i+6].val_b], scramble[tab[i+7].val_b]); |
| } else { |
| for (i=0; i<blen; i+=16) |
| fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,\n", |
| tab[i+0].val_b, tab[i+1].val_b, |
| tab[i+2].val_b, tab[i+3].val_b, |
| tab[i+4].val_b, tab[i+5].val_b, |
| tab[i+6].val_b, tab[i+7].val_b, |
| tab[i+8].val_b, tab[i+9].val_b, |
| tab[i+10].val_b, tab[i+11].val_b, |
| tab[i+12].val_b, tab[i+13].val_b, |
| tab[i+14].val_b, tab[i+15].val_b); |
| } |
| fprintf(f, "};\n"); |
| fprintf(f, "\n"); |
| } |
| } |
| |
| static void |
| perfect_dir(FILE *out, const char *which, const char *parser, dir_list *dirs) |
| { |
| ub4 nkeys; |
| key *keys; |
| hashform form; |
| bstuff *tab; /* table indexed by b */ |
| hstuff *tabh; /* table indexed by hash value */ |
| ub4 smax; /* scramble[] values in 0..smax-1, a power of 2 */ |
| ub4 alen; /* a in 0..alen-1, a power of 2 */ |
| ub4 blen; /* b in 0..blen-1, a power of 2 */ |
| ub4 salt; /* a parameter to the hash function */ |
| gencode final; /* code for final hash */ |
| ub4 i; |
| ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */ |
| char buf[10][80]; /* buffer for generated code */ |
| char *buf2[10]; /* also for generated code */ |
| int cpumode = strcmp(which, "cpu") == 0; |
| dir *d; |
| |
| /* perfect hash configuration */ |
| form.mode = NORMAL_HM; |
| form.hashtype = STRING_HT; |
| form.perfect = MINIMAL_HP; |
| form.speed = SLOW_HS; |
| |
| /* set up code for final hash */ |
| final.line = buf2; |
| final.used = 0; |
| final.len = 10; |
| for (i=0; i<10; i++) |
| final.line[i] = buf[i]; |
| |
| /* build list of keys */ |
| nkeys = 0; |
| keys = NULL; |
| STAILQ_FOREACH(d, dirs, link) { |
| key *k = yasm_xmalloc(sizeof(key)); |
| |
| k->name_k = yasm__xstrdup(d->name); |
| k->len_k = strlen(d->name); |
| k->next_k = keys; |
| keys = k; |
| nkeys++; |
| } |
| |
| /* find the hash */ |
| findhash(&tab, &tabh, &alen, &blen, &salt, &final, |
| scramble, &smax, keys, nkeys, &form); |
| |
| /* output the dir table: this should loop up to smax for NORMAL_HP, |
| * or up to pakd.nkeys for MINIMAL_HP. |
| */ |
| fprintf(out, "static const %s_parse_data %s_", which, which); |
| if (parser) |
| fprintf(out, "%s_", parser); |
| fprintf(out, "pd[%lu] = {\n", nkeys); |
| for (i=0; i<nkeys; i++) { |
| if (tabh[i].key_h) { |
| sval *sv; |
| STAILQ_FOREACH(d, dirs, link) { |
| if (strcmp(d->name, tabh[i].key_h->name_k) == 0) |
| break; |
| } |
| if (!d) { |
| report_error("internal error: could not find `%s'", |
| tabh[i].key_h->name_k); |
| break; |
| } |
| if (cpumode) |
| fprintf(out, "{\"%s\",", d->name); |
| else |
| fprintf(out, "%s(\"%s\",", d->func, d->name); |
| STAILQ_FOREACH(sv, &d->args, link) { |
| fprintf(out, " %s", sv->str); |
| if (STAILQ_NEXT(sv, link)) |
| fprintf(out, ","); |
| } |
| fprintf(out, cpumode ? "}" : ")"); |
| } else |
| fprintf(out, " { NULL }"); |
| |
| if (i < nkeys-1) |
| fprintf(out, ","); |
| fprintf(out, "\n"); |
| } |
| fprintf(out, "};\n"); |
| |
| /* output the hash tab[] array */ |
| make_c_tab(out, which, parser, tab, smax, blen, scramble); |
| |
| /* The hash function */ |
| fprintf(out, "#define tab %s_", which); |
| if (parser) |
| fprintf(out, "%s_", parser); |
| fprintf(out, "tab\n"); |
| fprintf(out, "static const %s_parse_data *\n%s_", which, which); |
| if (parser) |
| fprintf(out, "%s_", parser); |
| fprintf(out, "find(const char *key, size_t len)\n"); |
| fprintf(out, "{\n"); |
| fprintf(out, " const %s_parse_data *ret;\n", which); |
| for (i=0; i<final.used; ++i) |
| fprintf(out, final.line[i]); |
| fprintf(out, " if (rsl >= %lu) return NULL;\n", nkeys); |
| fprintf(out, " ret = &%s_", which); |
| if (parser) |
| fprintf(out, "%s_", parser); |
| fprintf(out, "pd[rsl];\n"); |
| fprintf(out, " if (strcmp(key, ret->name) != 0) return NULL;\n"); |
| fprintf(out, " return ret;\n"); |
| fprintf(out, "}\n"); |
| fprintf(out, "#undef tab\n\n"); |
| |
| free(tab); |
| free(tabh); |
| } |
| |
| /* Get an entire "real" line from the input file by combining any |
| * \\\n continuations. |
| */ |
| static int get_line(FILE *in) |
| { |
| char *p = line; |
| cur_line = next_line; |
| |
| if (feof(in)) |
| return 0; |
| |
| while (p < &line[1023-128]) { |
| if (!fgets(p, 128, in)) |
| return 1; |
| next_line++; |
| /* if continuation, strip out leading whitespace */ |
| if (p > line) { |
| char *p2 = p; |
| while (isspace(*p2)) p2++; |
| if (p2 > p) |
| memmove(p, p2, strlen(p2)+1); |
| } |
| while (*p) p++; |
| if (p[-2] != '\\' || p[-1] != '\n') { |
| if (p[-1] == '\n') |
| p[-1] = '\0'; |
| return 1; |
| } |
| p -= 2; |
| } |
| return 0; |
| } |
| |
| static struct { |
| const char *name; |
| int indx; |
| void (*handler) (void); |
| } directives[] = { |
| {"ARCH", ARCH, parse_arch}, |
| {"PARSERS", PARSERS, parse_parsers}, |
| {"INSN", INSN, parse_insn}, |
| {"CPU", CPU, parse_cpu}, |
| {"CPU_ALIAS", CPU_ALIAS, parse_cpu_alias}, |
| {"CPU_FEATURE", CPU_FEATURE, parse_cpu_feature}, |
| {"TARGETMOD", TARGETMOD, parse_targetmod}, |
| {"PREFIX", PREFIX, parse_prefix}, |
| {"REG", REG, parse_reg}, |
| {"REGGROUP", REGGROUP, parse_reggroup}, |
| {"SEGREG", SEGREG, parse_segreg}, |
| }; |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| FILE *in, *out; |
| size_t i; |
| char *tok; |
| int count[NUM_DIRS]; |
| dir_byp *db; |
| |
| for (i=0; i<NUM_DIRS; i++) |
| count[i] = 0; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: gap <in> <out>\n"); |
| return EXIT_FAILURE; |
| } |
| |
| in = fopen(argv[1], "rt"); |
| if (!in) { |
| fprintf(stderr, "Could not open `%s' for reading\n", argv[1]); |
| return EXIT_FAILURE; |
| } |
| |
| STAILQ_INIT(&insnprefix_byp); |
| STAILQ_INIT(&cpu_byp); |
| STAILQ_INIT(®tmod_byp); |
| |
| /* Parse input file */ |
| while (get_line(in)) { |
| int found; |
| /*printf("%s\n", line);*/ |
| tok = strtok(line, " \t\n"); |
| if (!tok) |
| continue; |
| |
| /* Comments start with # as the first thing on a line */ |
| if (tok[0] == '#') |
| continue; |
| |
| /* Look for directive */ |
| found = 0; |
| for (i=0; i<sizeof(directives)/sizeof(directives[0]); i++) { |
| if (strcmp(tok, directives[i].name) == 0) { |
| count[directives[i].indx]++; |
| directives[i].handler(); |
| found = 1; |
| break; |
| } |
| } |
| if (!found) |
| report_error("unknown directive `%s'\n", tok); |
| } |
| |
| /* Output some informational statistics */ |
| printf("Directives read:\n"); |
| for (i=0; i<sizeof(directives)/sizeof(directives[0]); i++) |
| printf("\t%d\t%s\n", count[directives[i].indx], directives[i].name); |
| |
| if (errors > 0) |
| return EXIT_FAILURE; |
| |
| out = fopen(argv[2], "wt"); |
| if (!out) { |
| fprintf(stderr, "Could not open `%s' for writing\n", argv[2]); |
| return EXIT_FAILURE; |
| } |
| |
| /* Get perfect hashes for the three lists of directives */ |
| STAILQ_FOREACH(db, &insnprefix_byp, link) |
| perfect_dir(out, "insnprefix", db->parser, &db->dirs); |
| STAILQ_FOREACH(db, &cpu_byp, link) |
| perfect_dir(out, "cpu", db->parser, &db->dirs); |
| STAILQ_FOREACH(db, ®tmod_byp, link) |
| perfect_dir(out, "regtmod", db->parser, &db->dirs); |
| |
| if (errors > 0) |
| return EXIT_FAILURE; |
| |
| return EXIT_SUCCESS; |
| } |
| |