diff options
Diffstat (limited to 'cgen/opc-itab.scm')
-rw-r--r-- | cgen/opc-itab.scm | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/cgen/opc-itab.scm b/cgen/opc-itab.scm new file mode 100644 index 00000000000..830b7e0083e --- /dev/null +++ b/cgen/opc-itab.scm @@ -0,0 +1,724 @@ +; Opcode table support. +; Copyright (C) 2000 Red Hat, Inc. +; This file is part of CGEN. + +; Append code here to be run before insn parsing/etc. +; These are for internal use and aren't intended to appear in .cpu files. +; ??? Nothing currently uses them but that might change. + +(define parse-init-code "") +(define insert-init-code "") +(define extract-init-code "") +(define print-init-code "") + +; Define CGEN_INIT_{PARSE,INSERT,EXTRACT,PRINT} macros. +; ??? These were early escape hatches. Not currently used. + +(define (-gen-init-macros) + (logit 2 "Generating init macros ...\n") + (string-append + "#define CGEN_INIT_PARSE(od) \\ +{\\\n" + parse-init-code + "}\n" + "#define CGEN_INIT_INSERT(od) \\ +{\\\n" + insert-init-code + "}\n" + "#define CGEN_INIT_EXTRACT(od) \\ +{\\\n" + extract-init-code + "}\n" + "#define CGEN_INIT_PRINT(od) \\ +{\\\n" + print-init-code + "}\n" + ) +) + +; Instruction field support. + +; Return C code to declare various ifield types,decls. + +(define (-gen-ifield-decls) + (logit 2 "Generating instruction field decls ...\n") + (string-append + "/* This struct records data prior to insertion or after extraction. */\n" + "struct cgen_fields\n{\n" + ; A special member `length' is used to record the length. + " int length;\n" + (string-map gen-ifield-value-decl (non-derived-ifields (current-ifld-list))) + "};\n\n" + ) +) + +; Instruction syntax support. + +; Extract the operand fields in SYNTAX-STRING. +; The result is a list of operand names. +; ??? Not currently used, but keep awhile. + +(define (extract-syntax-operands syntax) + (let loop ((syn syntax) (result nil)) + + (cond ((= (string-length syn) 0) + (reverse! result)) + + ((char=? #\\ (string-ref syn 0)) + (if (= (string-length syn) 1) + (error "missing char after '\\'" syntax)) + (loop (string-drop 2 syn) result)) + + ((char=? #\$ (string-ref syn 0)) + ; Extract the symbol from the string, which will be the name of + ; an operand. Append it to the result. + (if (= (string-length syn) 1) + (error "missing operand name" syntax)) + (if (char=? (string-ref syn 1) #\{) + (let ((n (chars-until-delimiter syn #\}))) + ; Note that 'n' includes the leading ${. + (case n + ((0) (error "empty operand name" syntax)) + ((#f) (error "missing '}'" syntax)) + (else (loop (string-drop (+ n 1) syn) + (cons (string->symbol (substring syn 2 n)) + result))))) + (let ((n (id-len (string-drop1 syn)))) + (if (= n 0) + (error "empty or invalid operand name" syntax)) + (loop (string-drop (1+ n) syn) + (cons (string->symbol (substring syn 1 (1+ n))) + result))))) + + (else (loop (string-drop1 syn) result)))) +) + +; Strip the mnemonic part from SYNTAX. +; (ie: everything up to but not including the first space or '$') +; If STRIP-MNEM-OPERANDS?, strip them too. + +(define (strip-mnemonic strip-mnem-operands? syntax) + (let ((space (string-index syntax #\space))) + (if strip-mnem-operands? + (if space + (string-drop space syntax) + "") + (let loop ((syn syntax)) + (if (= (string-length syn) 0) + "" + (case (string-ref syn 0) + ((#\space) syn) + ((#\\) (loop (string-drop 2 syn))) + ((#\$) syn) + (else (loop (string-drop1 syn)))))))) +) + +; Compute the sequence of syntax bytes for SYNTAX. +; STRIP-MNEMONIC? is #t if the mnemonic part is to be stripped off. +; STRIP-MNEM-OPERANDS? is #t if any mnemonic operands are to be stripped off. +; SYNTAX is a string of text and operands. +; OP-MACRO is the macro to call that computes an operand's value. +; The resulting syntax is expressed as a sequence of bytes. +; Values < 128 are characters that must be matched. +; Values >= 128 are 128 + the index into the operand table. + +(define (compute-syntax strip-mnemonic? strip-mnem-operands? syntax op-macro) + (let ((context "syntax computation") + (syntax (if strip-mnemonic? + (strip-mnemonic strip-mnem-operands? syntax) + syntax))) + + (let loop ((syn syntax) (result "")) + + (cond ((= (string-length syn) 0) + (string-append result "0")) + + ((char=? #\\ (string-ref syn 0)) + (if (= (string-length syn) 1) + (parse-error context "missing char after '\\'" syntax)) + (let ((escaped-char (string-ref syn 1)) + (remainder (string-drop 2 syn))) + (if (char=? #\\ escaped-char) + (loop remainder (string-append result "'\\\\', ")) + (loop remainder (string-append result "'" (string escaped-char) "', "))))) + + ((char=? #\$ (string-ref syn 0)) + ; Extract the symbol from the string, which will be the name of + ; an operand. Append it to the result. + (if (= (string-length syn) 1) + (parse-error context "missing operand name" syntax)) + ; Is it $foo or ${foo}? + (if (char=? (string-ref syn 1) #\{) + (let ((n (chars-until-delimiter syn #\}))) + ; Note that 'n' includes the leading ${. + ; FIXME: \} not implemented yet. + (case n + ((0) (parse-error context "empty operand name" syntax)) + ((#f) (parse-error context "missing '}'" syntax)) + (else (loop (string-drop (+ n 1) syn) + (string-append result op-macro " (" + (string-upcase + (gen-c-symbol + (substring syn 2 n))) + "), "))))) + (let ((n (id-len (string-drop1 syn)))) + (loop (string-drop (1+ n) syn) + (string-append result op-macro " (" + (string-upcase + (gen-c-symbol + (substring syn 1 (1+ n)))) + "), "))))) + + ; Append the character to the result. + (else (loop (string-drop1 syn) + (string-append result + "'" (string-take1 syn) "', ")))))) +) + +; Return C code to define the syntax string for SYNTAX +; MNEM is the C value to use to represent the instruction's mnemonic. +; OP is the C macro to use to compute an operand's syntax value. + +(define (gen-syntax-entry mnem op syntax) + (string-append + "{ { " + mnem ", " + ; `mnem' is used to represent the mnemonic, so we always want to strip it + ; from the syntax string, regardless of the setting of `strip-mnemonic?'. + (compute-syntax #t #f syntax op) + " } }") +) + +; Instruction format table support. + +; Return the table for IFMT, an <iformat> object. + +(define (-gen-ifmt-table-1 ifmt) + (gen-obj-sanitize + (ifmt-eg-insn ifmt) ; sanitize based on the example insn + (string-list + "static const CGEN_IFMT " (gen-sym ifmt) " = {\n" + " " + (number->string (ifmt-mask-length ifmt)) ", " + (number->string (ifmt-length ifmt)) ", " + "0x" (number->string (ifmt-mask ifmt) 16) ", " + "{ " + (string-list-map (lambda (ifld) + (string-list "{ F (" (ifld-enum ifld #f) ") }, ")) + (ifmt-ifields ifmt)) + "{ 0 } }\n};\n\n")) +) + +; Generate the insn format table. + +(define (-gen-ifmt-table) + (string-write + "/* Instruction formats. */\n\n" + "#define F(f) & @arch@_cgen_ifld_table[CONCAT2 (@ARCH@_,f)]\n\n" + (string-list-map -gen-ifmt-table-1 (current-ifmt-list)) + "#undef F\n\n" + ) +) + +; Parse/insert/extract/print handlers. +; Each handler type is recorded in the assembler/disassembler as an array of +; pointers to functions. The value recorded in the operand table is the index +; into this array. The first element in the array is reserved as index 0 is +; special (the "default"). +; +; The handlers are recorded here as associative lists in case we ever want +; to record more than just the name. +; +; Adding a new handler involves +; - specifying its name in the .cpu file +; - getting its name appended to these tables +; - writing the C code +; +; ??? It might be useful to define the handler in Scheme. Later. + +(define opc-parse-handlers '((insn-normal))) +(define opc-insert-handlers '((insn-normal))) +(define opc-extract-handlers '((insn-normal))) +(define opc-print-handlers '((insn-normal))) + +; FIXME: There currently isn't a spot for specifying special handlers for +; each instruction. For now assume we always use the same ones. + +(define (insn-handlers insn) + (string-append + (number->string (lookup-index 'insn-normal opc-parse-handlers 0)) + ", " + (number->string (lookup-index 'insn-normal opc-insert-handlers 0)) + ", " + (number->string (lookup-index 'insn-normal opc-extract-handlers 0)) + ", " + (number->string (lookup-index 'insn-normal opc-print-handlers 0)) + ) +) + +; Return C code to define the cgen_opcode_handler struct for INSN. +; This is intended to be the ultimate escape hatch for the parse/insert/ +; extract/print handlers. Each entry is an index into a table of handlers. +; The escape hatch isn't used yet. + +(define (gen-insn-handlers insn) + (string-append + "{ " + (insn-handlers insn) + " }" + ) +) + +; Handler table support. +; There are tables for each of parse/insert/extract/print. + +; Return C code to define the handler table for NAME with values VALUES. + +(define (gen-handler-table name values) + (string-append + "cgen_" name "_fn * const @arch@_cgen_" name "_handlers[] = \n{\n" + (string-map (lambda (elm) + (string-append " " name "_" + (gen-c-symbol (car elm)) + ",\n")) + values) + "};\n\n" + ) +) + +; Instruction table support. + +; Return a declaration of an enum for all insns. + +(define (-gen-insn-enum) + (logit 2 "Generating instruction enum ...\n") + (string-list + (gen-enum-decl 'cgen_insn_type "@arch@ instruction types" + "@ARCH@_INSN_" + (cons '(invalid) + (append (gen-obj-list-enums (non-multi-insns (current-insn-list))) + '((max))))) + "/* Index of `invalid' insn place holder. */\n" + "#define CGEN_INSN_INVALID @ARCH@_INSN_INVALID\n\n" + "/* Total number of insns in table. */\n" + "#define MAX_INSNS ((int) @ARCH@_INSN_MAX)\n\n" + ) +) + +; Return a reference to the format table entry of INSN. + +(define (gen-ifmt-entry insn) + (string-append "& " (gen-sym (insn-ifmt insn))) +) + +; Return the definition of an instruction value entry. + +(define (gen-ivalue-entry insn) + (string-list "{ " + "0x" (number->string (insn-value insn) 16) + (if #f ; (ifmt-opcodes-beyond-base? (insn-ifmt insn)) + (string-list ", { " + ; ??? wip: opcode values beyond the base insn + "0 }") + "") + " }") +) + +; Generate an insn opcode entry for INSN. +; ALL-ATTRS is a list of all instruction attributes. +; NUM-NON-BOOLS is the number of non-boolean insn attributes. + +(define (-gen-insn-opcode-entry insn all-attrs num-non-bools) + (gen-obj-sanitize + insn + (string-list + "/* " (insn-syntax insn) " */\n" + " {\n" + " " (gen-insn-handlers insn) ",\n" + " " (gen-syntax-entry "MNEM" "OP" (insn-syntax insn)) ",\n" + ; ??? 'twould save space to put a pointer here and record format separately + " " (gen-ifmt-entry insn) ", " + ;"0x" (number->string (insn-value insn) 16) ",\n" + (gen-ivalue-entry insn) "\n" + " },\n")) +) + +; Generate insn table. + +(define (-gen-insn-opcode-table) + (logit 2 "Generating instruction opcode table ...\n") + (let* ((all-attrs (current-insn-attr-list)) + (num-non-bools (attr-count-non-bools all-attrs))) + (string-write + "\ +#define A(a) (1 << CONCAT2 (CGEN_INSN_,a)) +#define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */ +#define OPERAND(op) CONCAT2 (@ARCH@_OPERAND_,op) +#define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field)) + +/* The instruction table. */ + +static const CGEN_OPCODE @arch@_cgen_insn_opcode_table[MAX_INSNS] = +{ + /* Special null first entry. + A `num' value of zero is thus invalid. + Also, the special `invalid' insn resides here. */ + { { 0, 0, 0, 0 }, {{0}}, 0, {0}},\n" + + (lambda () + (string-write-map (lambda (insn) + (logit 3 "Generating insn opcode entry for " (obj:name insn) " ...\n") + (-gen-insn-opcode-entry insn all-attrs + num-non-bools)) + (non-multi-insns (current-insn-list)))) + + "\ +}; + +#undef A +#undef MNEM +#undef OPERAND +#undef OP + +" + ) + ) +) + +; Return assembly/disassembly hashing support. + +(define (-gen-hash-fns) + (string-list + "\ +#ifndef CGEN_ASM_HASH_P +#define CGEN_ASM_HASH_P(insn) 1 +#endif + +#ifndef CGEN_DIS_HASH_P +#define CGEN_DIS_HASH_P(insn) 1 +#endif + +/* Return non-zero if INSN is to be added to the hash table. + Targets are free to override CGEN_{ASM,DIS}_HASH_P in the .opc file. */ + +static int +asm_hash_insn_p (insn) + const CGEN_INSN *insn; +{ + return CGEN_ASM_HASH_P (insn); +} + +static int +dis_hash_insn_p (insn) + const CGEN_INSN *insn; +{ + /* If building the hash table and the NO-DIS attribute is present, + ignore. */ + if (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_NO_DIS)) + return 0; + return CGEN_DIS_HASH_P (insn); +} + +#ifndef CGEN_ASM_HASH +#define CGEN_ASM_HASH_SIZE 127 +#ifdef CGEN_MNEMONIC_OPERANDS +#define CGEN_ASM_HASH(mnem) (*(unsigned char *) (mnem) % CGEN_ASM_HASH_SIZE) +#else +#define CGEN_ASM_HASH(mnem) (*(unsigned char *) (mnem) % CGEN_ASM_HASH_SIZE) /*FIXME*/ +#endif +#endif + +/* It doesn't make much sense to provide a default here, + but while this is under development we do. + BUFFER is a pointer to the bytes of the insn, target order. + VALUE is the first base_insn_bitsize bits as an int in host order. */ + +#ifndef CGEN_DIS_HASH +#define CGEN_DIS_HASH_SIZE 256 +#define CGEN_DIS_HASH(buf, value) (*(unsigned char *) (buf)) +#endif + +/* The result is the hash value of the insn. + Targets are free to override CGEN_{ASM,DIS}_HASH in the .opc file. */ + +static unsigned int +asm_hash_insn (mnem) + const char * mnem; +{ + return CGEN_ASM_HASH (mnem); +} + +/* BUF is a pointer to the bytes of the insn, target order. + VALUE is the first base_insn_bitsize bits as an int in host order. */ + +static unsigned int +dis_hash_insn (buf, value) + const char * buf; + CGEN_INSN_INT value; +{ + return CGEN_DIS_HASH (buf, value); +} +\n" + ) +) + +; Hash support decls. + +(define (-gen-hash-decls) + (string-list + "\ +/* The hash functions are recorded here to help keep assembler code out of + the disassembler and vice versa. */ + +static int asm_hash_insn_p PARAMS ((const CGEN_INSN *)); +static unsigned int asm_hash_insn PARAMS ((const char *)); +static int dis_hash_insn_p PARAMS ((const CGEN_INSN *)); +static unsigned int dis_hash_insn PARAMS ((const char *, CGEN_INSN_INT)); +\n" + ) +) + +; Macro insn support. + +; Return a macro-insn expansion entry. + +(define (-gen-miexpn-entry entry) + ; FIXME: wip + "0, " +) + +; Return a macro-insn table entry. +; ??? wip, not currently used. + +(define (-gen-minsn-table-entry minsn all-attrs num-non-bools) + (gen-obj-sanitize + minsn + (string-list + " /* " (minsn-syntax minsn) " */\n" + " {\n" + " " + "-1, " ; macro-insns are not currently enumerated, no current need to + "\"" (obj:name minsn) "\", " + "\"" (minsn-mnemonic minsn) "\",\n" + " " (gen-syntax-entry "MNEM" "OP" (minsn-syntax minsn)) ",\n" + " (PTR) & macro_" (gen-sym minsn) "_expansions[0],\n" + " " + (gen-obj-attr-defn 'minsn minsn all-attrs num-non-bools gen-insn-attr-mask) + "\n" + " },\n")) +) + +; Return a macro-insn opcode table entry. +; ??? wip, not currently used. + +(define (-gen-minsn-opcode-entry minsn all-attrs num-non-bools) + (gen-obj-sanitize + minsn + (string-list + " /* " (minsn-syntax minsn) " */\n" + " {\n" + " " + "-1, " ; macro-insns are not currently enumerated, no current need to + "\"" (obj:name minsn) "\", " + "\"" (minsn-mnemonic minsn) "\",\n" + " " (gen-syntax-entry "MNEM" "OP" (minsn-syntax minsn)) ",\n" + " (PTR) & macro_" (gen-sym minsn) "_expansions[0],\n" + " " + (gen-obj-attr-defn 'minsn minsn all-attrs num-non-bools gen-insn-attr-mask) + "\n" + " },\n")) +) + +; Macro insn expansion has one basic form, but we optimize the common case +; of unconditionally expanding the input text to one instruction. +; The general form is a Scheme expression that is interpreted at runtime to +; decide how to perform the expansion. Yes, that means having a (perhaps +; minimal) Scheme interpreter in the assembler. +; Another thing to do is have a builder for each real insn so instead of +; expanding to text, the macro-expansion could invoke the builder for each +; expanded-to insn. + +(define (-gen-macro-insn-table) + (logit 2 "Generating macro-instruction table ...\n") + (let* ((minsn-list (map (lambda (minsn) + (if (has-attr? minsn 'ALIAS) + (minsn-make-alias "gen-macro-insn-table" minsn) + minsn)) + (current-minsn-list))) + (all-attrs (current-insn-attr-list)) + (num-non-bools (attr-count-non-bools all-attrs))) + (string-write + "/* Formats for ALIAS macro-insns. */\n\n" + "#define F(f) & @arch@_cgen_ifld_table[CONCAT2 (@ARCH@_,f)]\n\n" + (lambda () + (string-write-map -gen-ifmt-table-1 + (map insn-ifmt (find (lambda (minsn) + (has-attr? minsn 'ALIAS)) + minsn-list)))) + "#undef F\n\n" + "/* Each non-simple macro entry points to an array of expansion possibilities. */\n\n" + (lambda () + (string-write-map (lambda (minsn) + (if (has-attr? minsn 'ALIAS) + "" + (string-append + "static const CGEN_MINSN_EXPANSION macro_" (gen-sym minsn) "_expansions[] =\n" + "{\n" + (string-map -gen-miexpn-entry + (minsn-expansions minsn)) + " { 0, 0 }\n};\n\n"))) + minsn-list)) + "\ +#define A(a) (1 << CONCAT2 (CGEN_INSN_,a)) +#define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */ +#define OPERAND(op) CONCAT2 (@ARCH@_OPERAND_,op) +#define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field)) + +/* The macro instruction table. */ + +static const CGEN_IBASE @arch@_cgen_macro_insn_table[] = +{ +" + (lambda () + (string-write-map (lambda (minsn) + (logit 3 "Generating macro-insn table entry for " (obj:name minsn) " ...\n") + ; Simple macro-insns are emitted as aliases of real insns. + (if (has-attr? minsn 'ALIAS) + (gen-insn-table-entry minsn all-attrs num-non-bools) + (-gen-minsn-table-entry minsn all-attrs num-non-bools))) + minsn-list)) + "\ +}; + +/* The macro instruction opcode table. */ + +static const CGEN_OPCODE @arch@_cgen_macro_insn_opcode_table[] = +{\n" + (lambda () + (string-write-map (lambda (minsn) + (logit 3 "Generating macro-insn table entry for " (obj:name minsn) " ...\n") + ; Simple macro-insns are emitted as aliases of real insns. + (if (has-attr? minsn 'ALIAS) + (-gen-insn-opcode-entry minsn all-attrs num-non-bools) + (-gen-minsn-opcode-entry minsn all-attrs num-non-bools))) + minsn-list)) + "\ +}; + +#undef A +#undef MNEM +#undef OPERAND +#undef OP +\n" + )) +) + +; Emit a function to call to initialize the opcode table. + +(define (-gen-opcode-init-fn) + (string-write + "\ +/* Set the recorded length of the insn in the CGEN_FIELDS struct. */ + +static void +set_fields_bitsize (fields, size) + CGEN_FIELDS *fields; + int size; +{ + CGEN_FIELDS_BITSIZE (fields) = size; +} + +/* Function to call before using the operand instance table. + This plugs the opcode entries and macro instructions into the cpu table. */ + +void +@arch@_cgen_init_opcode_table (cd) + CGEN_CPU_DESC cd; +{ + int i; + int num_macros = (sizeof (@arch@_cgen_macro_insn_table) / + sizeof (@arch@_cgen_macro_insn_table[0])); + const CGEN_IBASE *ib = & @arch@_cgen_macro_insn_table[0]; + const CGEN_OPCODE *oc = & @arch@_cgen_macro_insn_opcode_table[0]; + CGEN_INSN *insns = (CGEN_INSN *) xmalloc (num_macros * sizeof (CGEN_INSN)); + memset (insns, 0, num_macros * sizeof (CGEN_INSN)); + for (i = 0; i < num_macros; ++i) + { + insns[i].base = &ib[i]; + insns[i].opcode = &oc[i]; + } + cd->macro_insn_table.init_entries = insns; + cd->macro_insn_table.entry_size = sizeof (CGEN_IBASE); + cd->macro_insn_table.num_init_entries = num_macros; + + oc = & @arch@_cgen_insn_opcode_table[0]; + insns = (CGEN_INSN *) cd->insn_table.init_entries; + for (i = 0; i < MAX_INSNS; ++i) + insns[i].opcode = &oc[i]; + + cd->sizeof_fields = sizeof (CGEN_FIELDS); + cd->set_fields_bitsize = set_fields_bitsize; + + cd->asm_hash_p = asm_hash_insn_p; + cd->asm_hash = asm_hash_insn; + cd->asm_hash_size = CGEN_ASM_HASH_SIZE; + + cd->dis_hash_p = dis_hash_insn_p; + cd->dis_hash = dis_hash_insn; + cd->dis_hash_size = CGEN_DIS_HASH_SIZE; +} +" + ) +) + +; Top level C code generators + +; FIXME: Create enum objects for all the enums we explicitly declare here. +; Then they'd be usable and we wouldn't have to special case them here. + +(define (cgen-opc.h) + (logit 1 "Generating " (current-arch-name) "-opc.h ...\n") + (string-write + (gen-copyright "Instruction opcode header for @arch@." + CURRENT-COPYRIGHT CURRENT-PACKAGE) + "\ +#ifndef @ARCH@_OPC_H +#define @ARCH@_OPC_H + +" + (lambda () (gen-extra-opc.h srcdir (current-arch-name))) ; from <arch>.opc + -gen-insn-enum + -gen-ifield-decls + -gen-init-macros + " + +#endif /* @ARCH@_OPC_H */ +" + ) +) + +; This file contains the instruction opcode table. + +(define (cgen-opc.c) + (logit 1 "Generating " (current-arch-name) "-opc.c ...\n") + (string-write + (gen-copyright "Instruction opcode table for @arch@." + CURRENT-COPYRIGHT CURRENT-PACKAGE) + "\ +#include \"sysdep.h\" +#include \"ansidecl.h\" +#include \"bfd.h\" +#include \"symcat.h\" +#include \"@prefix@-desc.h\" +#include \"@prefix@-opc.h\" +#include \"libiberty.h\" +\n" + (lambda () (gen-extra-opc.c srcdir (current-arch-name))) ; from <arch>.opc + -gen-hash-decls + -gen-ifmt-table + -gen-insn-opcode-table + -gen-macro-insn-table + -gen-hash-fns + -gen-opcode-init-fn + ) +) |