root/sys/arch/hpc/hpc/platid_gen/platid_gen.c
/*      $NetBSD: platid_gen.c,v 1.11 2014/03/26 17:54:46 christos Exp $ */

/*-
 * Copyright (c) 1999
 *         Shin Takemura and PocketBSD Project. All rights reserved.
 *
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the PocketBSD project
 *      and its contributors.
 * 4. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND 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 REGENTS OR 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: platid_gen.c,v 1.11 2014/03/26 17:54:46 christos Exp $");

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

#include "platid_gen.h"

/*
 * constants
 */
#define SEARCH_IGNORECASE       (1<<0)

#define NMODES  2
#define MAXNEST 4
#define MAXLEN  1024

enum { FORM_GENHDR, FORM_MASK_H, FORM_MASK_C, FORM_NAME_C, FORM_PARSE_ONLY };

/*
 * data type definitions
 */
struct genctx_t {
        int num;
        const char *alt;
        const char *node_name[2];
        char sym[MAXLEN];
        char name[MAXLEN];
} genctx[NMODES][MAXNEST];

/*
 * function prototypes
 */
void    gen_list(node_t *);
void    gen_output(void);
void    gen_header(void);
void    gen_mask_h(void);
void    gen_mask_c(void);
void    gen_name_c(void);
void    gen_comment(FILE *);
void    enter(void);
void    leave(void);

/*
 * global data
 */
node_t* def_tree;
int nest;
int mode;
FILE *fp_out;
int form;
int count;

#define MODE_INVALID    -1
#define MODE_CPU        0
#define MODE_MACHINE    1
char* mode_names[] = {
        "CPU", "MACHINE", NULL
};
#define PREFIX  "PLATID"
char* prefix_names[] = {
        "CPU", "MACH",
};
char* shift_names[NMODES][MAXNEST] = {
        {
                "PLATID_CPU_ARCH_SHIFT",
                "PLATID_CPU_SERIES_SHIFT",
                "PLATID_CPU_MODEL_SHIFT",
                "PLATID_CPU_SUBMODEL_SHIFT",
        },
        {
                "PLATID_VENDOR_SHIFT",
                "PLATID_SERIES_SHIFT",
                "PLATID_MODEL_SHIFT",
                "PLATID_SUBMODEL_SHIFT",
        },
};

/*
 * program entry
 */
int
main(int argc, char *argv[])
{
        int i;

        form = FORM_GENHDR;
        fp_out = stdout;
        count = 0;

        for (i = 1; i < argc; i++) {
                if (strcmp(argv[i], "-header") == 0) {
                        form = FORM_GENHDR;
                } else if (strcmp(argv[i], "-mask_h") == 0) {
                        form = FORM_MASK_H;
                } else if (strcmp(argv[i], "-mask_c") == 0) {
                        form = FORM_MASK_C;
                } else if (strcmp(argv[i], "-name_c") == 0) {
                        form = FORM_NAME_C;
                } else if (strcmp(argv[i], "-parse_only") == 0) {
                        form = FORM_PARSE_ONLY;
                } else {
                usage:
                        fprintf(stderr, "usage platid_gen <option>\n");
                        fprintf(stderr, "  option: -header\n");
                        fprintf(stderr, "          -mask_h\n");
                        fprintf(stderr, "          -mask_c\n");
                        fprintf(stderr, "          -name_c\n");
                        fprintf(stderr, "          -parse_only\n");
                        exit(1);
                }
        }

        if (read_def()) {
                exit(1);
        }

        if (form == FORM_PARSE_ONLY) {
                dump_node("", def_tree);
                exit (0);
        }

        gen_comment(fp_out);
        switch (form) {
        case FORM_GENHDR:
                break;
        case FORM_MASK_H:
                break;
        case FORM_MASK_C:
                fprintf(fp_out, "#include <machine/platid.h>\n");
                fprintf(fp_out, "#include <machine/platid_mask.h>\n");
                break;
        case FORM_NAME_C:
                fprintf(fp_out, "#include <machine/platid.h>\n");
                fprintf(fp_out, "#include <machine/platid_mask.h>\n");
                fprintf(fp_out,"struct platid_name platid_name_table[] = {\n");
                break;
        }

        nest = -1;
        enter(); /* XXX */
        mode = MODE_INVALID;
        genctx[MODE_CPU][nest].alt = NULL;
        genctx[MODE_MACHINE][nest].alt = NULL;
        gen_list((node_t*)def_tree->ptr1);

        switch (form) {
        case FORM_GENHDR:
        case FORM_MASK_H:
        case FORM_MASK_C:
                break;
        case FORM_NAME_C:
                fprintf(fp_out, "};\n");
                fprintf(fp_out, "int platid_name_table_size = sizeof(platid_name_table)/sizeof(*platid_name_table);\n");
                break;
        }
        fclose(fp_out);

        exit(0);
}

int
table_getnum(char **table, const char *s, int def, int opt)
{
        int num;

        num = 0;
        while (table[num]) {
                int diff;
                if (opt & SEARCH_IGNORECASE) {
                        diff = strcmp(table[num], s);
                } else {
                        diff = strcasecmp(table[num], s);
                }
                if (diff == 0) {
                        return (num);
                }
                num++;
        }
        return def;
}

#define GET_MODE(s) \
        table_getnum(mode_names, (s), MODE_INVALID, SEARCH_IGNORECASE)
#define GET_ALT(s) \
        table_getnum(mode_names, (s), MODE_INVALID, SEARCH_IGNORECASE)

void
enter(void)
{
        nest++;
        if (MAXNEST <= nest) {
                fprintf(stderr, "too much nest\n");
                exit(1);
        }
        genctx[mode][nest].num = 0;
        genctx[mode][nest].alt = NULL;
        genctx[mode][nest].node_name[0] = NULL;
        genctx[mode][nest].node_name[1] = NULL;
        if (0 < nest) {
                genctx[mode][nest].alt = genctx[mode][nest - 1].alt;
        }
}

void
leave(void)
{
        nest--;
        if (nest < 0) {
                fprintf(stderr, "internal error (nest=%d)\n", nest);
                exit(1);
        }
}

void
gen_comment(FILE *fp)
{
        fprintf(fp, "/*\n");
        fprintf(fp, " *  Do not edit.\n");
        fprintf(fp, " *  This file is automatically generated by platid.awk.\n");
        fprintf(fp, " */\n");
}

int
gen_name(char *buf, struct genctx_t ctx[], int nest, int name, char *punct,
    int ignr)
{
        int i;
        buf[0] = '\0';
        for (i = 0; i <= nest; i++) {
                if (!(ignr <= i && nest != i)) {
                        if (i != 0) {
                                strcat(buf, punct);
                        }
                        strcat(buf, ctx[i].node_name[name]);
                }
        }
}

void
gen_list(node_t* np)
{
        int i, t;

        for ( ; np; np = np->link) {
                switch (np->type) {
                case N_LABEL:
                        if ((mode = GET_MODE(np->ptr1)) == MODE_INVALID) {
                                fprintf(stderr, "invalid mode '%s'\n",
                                    np->ptr1);
                                exit(1);
                        }
                        break;
                case N_MODIFIER:
                        t = GET_ALT(np->ptr1);
                        if (t == MODE_INVALID) {
                                fprintf(stderr, "unknown alternater '%s'\n",
                                    np->ptr1);
                                exit(1);
                        }
                        if (t == mode) {
                                fprintf(stderr,
                                    "invalid alternater '%s' (ignored)\n",
                                    np->ptr1);
                                exit(1);
                        }
                        genctx[mode][nest].alt = np->ptr2;
                        break;
                case N_ENTRY:
                        if (np->ptr2 == NULL) {
                                char buf[MAXLEN];
                                snprintf(buf, sizeof(buf), "%s%s",
                                    nest == 0 ? "" : " ",
                                    np->ptr1);
                                np->ptr2 = strdup(buf);
                                if (nest == 3)
                                        np->val = 1;
                        }
                        touppers((char*)np->ptr1);

                        genctx[mode][nest].num++;
                        genctx[mode][nest].node_name[0] = np->ptr1;
                        genctx[mode][nest].node_name[1] = np->ptr2;
                        gen_name(genctx[mode][nest].sym, genctx[mode],
                            nest, 0, "_", nest == 3 ? 2 : nest);
                        gen_name(genctx[mode][nest].name, genctx[mode],
                            nest, 1, "", nest - np->val);
                        gen_output();
                        break;
                case N_LIST:
                        enter();
                        gen_list((node_t*)np->ptr1);
                        leave();
                        break;
                case N_DIRECTIVE:
                        fprintf(fp_out, "%s", np->ptr1);
                        break;
                default:
                        fprintf(stderr, "internal error (type=%d)\n", np->type);
                        exit(1);
                        break;
                }
        }
}

void
gen_output(void)
{
        switch (form) {
        case FORM_GENHDR:
                gen_header();
                break;
        case FORM_MASK_H:
                gen_mask_h();
                break;
        case FORM_MASK_C:
                gen_mask_c();
                break;
        case FORM_NAME_C:
                gen_name_c();
                break;
        }
}

/*
 * platid_generated.h
 *
 * #define PLATID_CPU_XXX_NUM   1
 * #define PLATID_CPU_XXX       \
 *   ((PLATID_CPU_XXX_NUM << PLATID_CPU_ARCH_SHIFT))
 * #define PLATID_CPU_XXX_YYY   \
 *   ((PLATID_CPU_XXX_YYY_NUM << PLATID_CPU_SERIES_SHIFT)| \
 *     PLATID_CPU_XXX)
 *
 * #ifndef SPEC_PLATFORM
 * #define SPEC_MACH_XXX
 * #endif 
 * #define PLATID_MACH_XXX_NUM  1
 * #define PLATID_MACH_XXX      \
 *   ((PLATID_MACH_XXX_NUM << PLATID_MACH_ARCH_SHIFT))
 * #define PLATID_MACH_XXX_YYY  \
 *   ((PLATID_MACH_XXX_YYY_NUM << PLATID_MACH_SERIES_SHIFT)| \
 *     PLATID_MACH_XXX)
 */
void
gen_header(void)
{
        char *prefix = prefix_names[mode];
        char *name = genctx[mode][nest].sym;

        if (mode == MODE_MACHINE) {
                fprintf(fp_out, "#ifndef SPEC_PLATFORM\n");
                fprintf(fp_out, "#define %s_%s_%s\n", "SPEC", prefix, name);
                fprintf(fp_out, "#endif /* !SPEC_PLATFORM */\n");
        }
        fprintf(fp_out, "#define %s_%s_%s_NUM\t%d\n", PREFIX, prefix, name,
            genctx[mode][nest].num);
        fprintf(fp_out, "#define %s_%s_%s\t\\\n", PREFIX, prefix, name);
        fprintf(fp_out, "  ((%s_%s_%s_NUM << %s)", PREFIX, prefix, name,
            shift_names[mode][nest]);
        if (0 < nest) {
                fprintf(fp_out, "| \\\n    %s_%s_%s",
                    PREFIX, prefix, genctx[mode][nest - 1].sym);
        }
        fprintf(fp_out, ")\n");
}

/*
 * platid_mask.h:
 *
 * extern platid_t platid_mask_CPU_MIPS;
 * #ifdef PLATID_DEFINE_MASK_NICKNAME
 * #  define GENERIC_MIPS ((int)&platid_mask_CPU_MIPS)
 * #endif
 */
void
gen_mask_h(void)
{
        char *name = genctx[mode][nest].sym;

        fprintf(fp_out, "extern platid_t platid_mask_%s_%s;\n",
            prefix_names[mode], name);
        fprintf(fp_out, "#ifdef PLATID_DEFINE_MASK_NICKNAME\n");
        fprintf(fp_out, "#  define %s%s ((int)&platid_mask_%s_%s)\n",
            (mode == MODE_CPU)?"GENERIC_":"",
            name, prefix_names[mode], name);
        fprintf(fp_out, "#endif\n");
}

/*
 * platid_mask.c:
 *
 * platid_t platid_mask_CPU_MIPS = {{
 *      PLATID_CPU_MIPS,
 *      PLATID_WILD
 * }};
 */
void
gen_mask_c(void)
{
        char *name = genctx[mode][nest].sym;

        fprintf(fp_out, "platid_t platid_mask_%s_%s = {{\n",
            prefix_names[mode], name);
        switch (mode) {
        case MODE_CPU:
                fprintf(fp_out, "\t%s_CPU_%s,\n", PREFIX, name);
                if (genctx[mode][nest].alt == NULL)
                        fprintf(fp_out, "\t%s_WILD\n", PREFIX);
                else
                        fprintf(fp_out, "\t%s_MACH_%s,\n", PREFIX,
                            genctx[mode][nest].alt);
                break;
        case MODE_MACHINE:
                if (genctx[mode][nest].alt == NULL)
                        fprintf(fp_out, "\t%s_WILD,\n", PREFIX);
                else
                        fprintf(fp_out, "\t%s_CPU_%s,\n", PREFIX,
                            genctx[mode][nest].alt);
                fprintf(fp_out, "\t%s_MACH_%s\n", PREFIX, name);
                break;
        }
        fprintf(fp_out, "}};\n");
}

/*
 * platid_name.c:
 */
void
gen_name_c(void)
{
        fprintf(fp_out, "\t{ &platid_mask_%s_%s,\n",
            prefix_names[mode], genctx[mode][nest].sym);
        fprintf(fp_out, "\t TEXT(\"%s\") },\n", genctx[mode][nest].name);
        count++;
}