diff options
author | Nick Clifton <nickc@redhat.com> | 2003-04-01 15:50:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2003-04-01 15:50:31 +0000 |
commit | e0001a05d2e4967ee86f4468cdc4fafea66b92d1 (patch) | |
tree | 4676b72e452f4dfc81e8d6646fb43f63a108da1b /gas/config/xtensa-relax.c | |
parent | ce0c72625ad0f6497718b4293572b2b6be711714 (diff) | |
download | fsf-binutils-gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.zip fsf-binutils-gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.gz fsf-binutils-gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.bz2 |
Add Xtensa port
Diffstat (limited to 'gas/config/xtensa-relax.c')
-rw-r--r-- | gas/config/xtensa-relax.c | 1766 |
1 files changed, 1766 insertions, 0 deletions
diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c new file mode 100644 index 0000000..47aa03c --- /dev/null +++ b/gas/config/xtensa-relax.c @@ -0,0 +1,1766 @@ +/* Table of relaxations for Xtensa assembly. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS 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 2, or (at your option) + any later version. + + GAS 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 GAS; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. */ + +/* This file contains the code for generating runtime data structures + for relaxation pattern matching from statically specified strings. + Each action contains an instruction pattern to match and + preconditions for the match as well as an expansion if the pattern + matches. The preconditions can specify that two operands are the + same or an operand is a specific constant. The expansion uses the + bound variables from the pattern to specify that specific operands + from the pattern should be used in the result. + + The patterns match a language like: + + INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )* + INSN_TEMPL ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ] + OPCODE ::= id + OPERAND ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')' + SPECIALFN ::= 'HI24S' | 'F32MINUS' | 'LOW8' + VARIABLE ::= '%' id + PRECOND ::= OPERAND CMPOP OPERAND + CMPOP ::= '==' | '!=' + + The replacement language + INSN_REPL ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )* + INSN_LABEL_LIT ::= INSN_TEMPL + | 'LABEL' num + | 'LITERAL' num ' ' VARIABLE + + The operands in a PRECOND must be constants or variables bound by + the INSN_PATTERN. + + The operands in the INSN_REPL must be constants, variables bound in + the associated INSN_PATTERN, special variables that are bound in + the INSN_REPL by LABEL or LITERAL definitions, or special value + manipulation functions. + + A simple example of a replacement pattern: + {"movi.n %as,%imm", "movi %as,%imm"} would convert the narrow + movi.n instruction to the wide movi instruction. + + A more complex example of a branch around: + {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"} + would convert a branch to a negated branch to the following instruction + with a jump to the original label. + + An Xtensa-specific example that generates a literal: + {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"} + will convert a movi instruction to an l32r of a literal + literal defined in the literal pool. + + Even more complex is a conversion of a load with immediate offset + to a load of a freshly generated literal, an explicit add and + a load with 0 offset. This transformation is only valid, though + when the first and second operands are not the same as specified + by the "| %at!=%as" precondition clause. + {"l32i %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"} + + There is special case for loop instructions here, but because we do + not currently have the ability to represent the difference of two + symbols, the conversion requires special code in the assembler to + write the operands of the addi/addmi pair representing the + difference of the old and new loop end label. */ + +#include "as.h" +#include "xtensa-isa.h" +#include "xtensa-relax.h" +#include <stddef.h> + +/* Imported from bfd. */ +extern xtensa_isa xtensa_default_isa; + + +/* The opname_list is a small list of names that we use for opcode and + operand variable names to simplify ownership of these commonly used + strings. Strings entered in the table can be compared by pointer + equality. */ + +typedef struct opname_list_struct opname_list; +typedef opname_list opname_e; + +struct opname_list_struct +{ + char *opname; + opname_list *next; +}; + +static opname_list *local_opnames = NULL; + + +/* The "opname_map" and its element structure "opname_map_e" are used + for binding an operand number to a name or a constant. */ + +typedef struct opname_map_e_struct opname_map_e; +typedef struct opname_map_struct opname_map; + +struct opname_map_e_struct +{ + const char *operand_name; /* If null, then use constant_value. */ + size_t operand_num; + unsigned constant_value; + opname_map_e *next; +}; + +struct opname_map_struct +{ + opname_map_e *head; + opname_map_e **tail; +}; + +/* The "precond_list" and its element structure "precond_e" represents + explicit preconditions comparing operand variables and constants. + In the "precond_e" structure, a variable is identified by the name + in the "opname" field. If that field is NULL, then the operand + is the constant in field "opval". */ + +typedef struct precond_e_struct precond_e; +typedef struct precond_list_struct precond_list; + +struct precond_e_struct +{ + const char *opname1; + unsigned opval1; + CmpOp cmpop; + const char *opname2; + unsigned opval2; + precond_e *next; +}; + +struct precond_list_struct +{ + precond_e *head; + precond_e **tail; +}; + + +/* The insn_templ represents the INSN_TEMPL instruction template. It + is an opcode name with a list of operands. These are used for + instruction patterns and replacement patterns. */ + +typedef struct insn_templ_struct insn_templ; +struct insn_templ_struct +{ + const char *opcode_name; + opname_map operand_map; +}; + + +/* The insn_pattern represents an INSN_PATTERN instruction pattern. + It is an instruction template with preconditions that specify when + it actually matches a given instruction. */ + +typedef struct insn_pattern_struct insn_pattern; +struct insn_pattern_struct +{ + insn_templ t; + precond_list preconds; +}; + + +/* The "insn_repl" and associated element structure "insn_repl_e" + instruction replacement list is a list of + instructions/LITERALS/LABELS with constant operands or operands + with names bound to the operand names in the associated pattern. */ + +typedef struct insn_repl_e_struct insn_repl_e; +struct insn_repl_e_struct +{ + insn_templ t; + insn_repl_e *next; +}; + +typedef struct insn_repl_struct insn_repl; +struct insn_repl_struct +{ + insn_repl_e *head; + insn_repl_e **tail; +}; + + +/* The split_rec is a vector of allocated char * pointers. */ + +typedef struct split_rec_struct split_rec; +struct split_rec_struct +{ + char **vec; + size_t count; +}; + +/* The "string_pattern_pair" is a set of pairs containing instruction + patterns and replacement strings. */ + +typedef struct string_pattern_pair_struct string_pattern_pair; +struct string_pattern_pair_struct +{ + const char *pattern; + const char *replacement; +}; + + +/* The widen_spec_list is a list of valid substitutions that generate + wider representations. These are generally used to specify + replacements for instructions whose immediates do not fit their + encodings. A valid transition may require mutiple steps of + one-to-one instruction replacements with a final multiple + instruction replacement. As an example, here are the transitions + required to replace an 'addi.n' with an 'addi', 'addmi'. + + addi.n a4, 0x1010 + => addi a4, 0x1010 + => addmi a4, 0x1010 + => addmi a4, 0x1000, addi a4, 0x10. */ + +static string_pattern_pair widen_spec_list[] = +{ + {"add.n %ar,%as,%at", "add %ar,%as,%at"}, + {"addi.n %ar,%as,%imm", "addi %ar,%as,%imm"}, + {"beqz.n %as,%label", "beqz %as,%label"}, + {"bnez.n %as,%label", "bnez %as,%label"}, + {"l32i.n %at,%as,%imm", "l32i %at,%as,%imm"}, + {"mov.n %at,%as", "or %at,%as,%as"}, + {"movi.n %as,%imm", "movi %as,%imm"}, + {"nop.n", "or 1,1,1"}, + {"ret.n", "ret"}, + {"retw.n", "retw"}, + {"s32i.n %at,%as,%imm", "s32i %at,%as,%imm"}, + {"srli %at,%as,%imm", "extui %at,%as,%imm,F32MINUS(%imm)"}, + {"slli %ar,%as,0", "or %ar,%as,%as"}, + /* Widening with literals */ + {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"}, + {"addi %ar,%as,%imm", "addmi %ar,%as,%imm"}, + /* LOW8 is the low 8 bits of the Immed + MID8S is the middle 8 bits of the Immed */ + {"addmi %ar,%as,%imm", "addmi %ar,%as,HI24S(%imm); addi %ar,%ar,LOW8(%imm)"}, + {"addmi %ar,%as,%imm | %ar!=%as", + "LITERAL0 %imm; l32r %ar,%LITERAL0; add %ar,%as,%ar"}, + + /* Widening the load instructions with too-large immediates */ + {"l8ui %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l8ui %at,%at,0"}, + {"l16si %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16si %at,%at,0"}, + {"l16ui %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16ui %at,%at,0"}, +#if 0 /* Xtensa Synchronization Option not yet available */ + {"l32ai %at,%as,%imm", + "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32ai %at,%at,0"}, +#endif +#if 0 /* Xtensa Speculation Option not yet available */ + {"l32is %at,%as,%imm", + "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32is %at,%at,0"}, +#endif + {"l32i %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"}, + + /* This is only PART of the loop instruction. In addition, hard + coded into it's use is a modification of the final operand in the + instruction in bytes 9 and 12. */ + {"loop %as,%label", + "loop %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + {"loopgtz %as,%label", + "beqz %as,%label;" + "bltz %as,%label;" + "loopgtz %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + {"loopnez %as,%label", + "beqz %as,%label;" + "loopnez %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + +#if 0 /* no mechanism here to determine if Density Option is available */ + {"beqz %as,%label", "bnez.n %as,%LABEL0;j %label;LABEL0"}, + {"bnez %as,%label", "beqz.n %as,%LABEL0;j %label;LABEL0"}, +#else + {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"}, + {"bnez %as,%label", "beqz %as,%LABEL0;j %label;LABEL0"}, +#endif + + {"bgez %as,%label", "bltz %as,%LABEL0;j %label;LABEL0"}, + {"bltz %as,%label", "bgez %as,%LABEL0;j %label;LABEL0"}, + {"beqi %as,%imm,%label", "bnei %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bnei %as,%imm,%label", "beqi %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bgei %as,%imm,%label", "blti %as,%imm,%LABEL0;j %label;LABEL0"}, + {"blti %as,%imm,%label", "bgei %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bgeui %as,%imm,%label", "bltui %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bltui %as,%imm,%label", "bgeui %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bbci %as,%imm,%label", "bbsi %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL0;j %label;LABEL0"}, + {"beq %as,%at,%label", "bne %as,%at,%LABEL0;j %label;LABEL0"}, + {"bne %as,%at,%label", "beq %as,%at,%LABEL0;j %label;LABEL0"}, + {"bge %as,%at,%label", "blt %as,%at,%LABEL0;j %label;LABEL0"}, + {"blt %as,%at,%label", "bge %as,%at,%LABEL0;j %label;LABEL0"}, + {"bgeu %as,%at,%label", "bltu %as,%at,%LABEL0;j %label;LABEL0"}, + {"bltu %as,%at,%label", "bgeu %as,%at,%LABEL0;j %label;LABEL0"}, + {"bany %as,%at,%label", "bnone %as,%at,%LABEL0;j %label;LABEL0"}, +#if 1 /* provide relaxations for Boolean Option */ + {"bt %bs,%label", "bf %bs,%LABEL0;j %label;LABEL0"}, + {"bf %bs,%label", "bt %bs,%LABEL0;j %label;LABEL0"}, +#endif + {"bnone %as,%at,%label", "bany %as,%at,%LABEL0;j %label;LABEL0"}, + {"ball %as,%at,%label", "bnall %as,%at,%LABEL0;j %label;LABEL0"}, + {"bnall %as,%at,%label", "ball %as,%at,%LABEL0;j %label;LABEL0"}, + {"bbc %as,%at,%label", "bbs %as,%at,%LABEL0;j %label;LABEL0"}, + {"bbs %as,%at,%label", "bbc %as,%at,%LABEL0;j %label;LABEL0"}, + {"call0 %label", "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0"}, + {"call4 %label", "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4"}, + {"call8 %label", "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8"}, + {"call12 %label", "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12"} +}; + +#define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair)) + + +/* The simplify_spec_list specifies simplifying transformations that + will reduce the instruction width or otherwise simplify an + instruction. These are usually applied before relaxation in the + assembler. It is always legal to simplify. Even for "addi as, 0", + the "addi.n as, 0" will eventually be widened back to an "addi 0" + after the widening table is applied. Note: The usage of this table + has changed somewhat so that it is entirely specific to "narrowing" + instructions to use the density option. This table is not used at + all when the density option is not available. */ + +string_pattern_pair simplify_spec_list[] = +{ + {"add %ar,%as,%at", "add.n %ar,%as,%at"}, + {"addi.n %ar,%as,0", "mov.n %ar,%as"}, + {"addi %ar,%as,0", "mov.n %ar,%as"}, + {"addi %ar,%as,%imm", "addi.n %ar,%as,%imm"}, + {"addmi %ar,%as,%imm", "addi.n %ar,%as,%imm"}, + {"beqz %as,%label", "beqz.n %as,%label"}, + {"bnez %as,%label", "bnez.n %as,%label"}, + {"l32i %at,%as,%imm", "l32i.n %at,%as,%imm"}, + {"movi %as,%imm", "movi.n %as,%imm"}, + {"or %ar,%as,%at | %as==%at", "mov.n %ar,%as"}, + {"ret", "ret.n"}, + {"retw", "retw.n"}, + {"s32i %at,%as,%imm", "s32i.n %at,%as,%imm"}, + {"slli %ar,%as,0", "mov.n %ar,%as"} +}; + +#define SIMPLIFY_COUNT \ + (sizeof (simplify_spec_list) / sizeof (string_pattern_pair)) + + +/* Transition generation helpers. */ + +static void append_transition + PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *)); +static void append_condition + PARAMS ((TransitionRule *, Precondition *)); +static void append_value_condition + PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned)); +static void append_constant_value_condition + PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned)); +static void append_build_insn + PARAMS ((TransitionRule *, BuildInstr *)); +static void append_op + PARAMS ((BuildInstr *, BuildOp *)); +static void append_literal_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_label_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_constant_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_field_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_user_fn_field_op + PARAMS ((BuildInstr *, unsigned, OpType, unsigned)); +static long operand_function_HI24S + PARAMS ((long)); +static long operand_function_F32MINUS + PARAMS ((long)); +static long operand_function_LOW8 + PARAMS ((long)); + +/* Externally visible functions. */ + +extern bfd_boolean xg_has_userdef_op_fn + PARAMS ((OpType)); +extern long xg_apply_userdef_op_fn + PARAMS ((OpType, long)); + +/* Parsing helpers. */ + +static const char *enter_opname_n + PARAMS ((const char *, size_t)); +static const char *enter_opname + PARAMS ((const char *)); + +/* Construction and destruction. */ + +static void init_opname_map + PARAMS ((opname_map *)); +static void clear_opname_map + PARAMS ((opname_map *)); +static void init_precond_list + PARAMS ((precond_list *)); +static void clear_precond_list + PARAMS ((precond_list *)); +static void init_insn_templ + PARAMS ((insn_templ *)); +static void clear_insn_templ + PARAMS ((insn_templ *)); +static void init_insn_pattern + PARAMS ((insn_pattern *)); +static void clear_insn_pattern + PARAMS ((insn_pattern *)); +static void init_insn_repl + PARAMS ((insn_repl *)); +static void clear_insn_repl + PARAMS ((insn_repl *)); +static void init_split_rec + PARAMS ((split_rec *)); +static void clear_split_rec + PARAMS ((split_rec *)); + +/* Operand and insn_templ helpers. */ + +static bfd_boolean same_operand_name + PARAMS ((const opname_map_e *, const opname_map_e *)); +static opname_map_e *get_opmatch + PARAMS ((opname_map *, const char *)); +static bfd_boolean op_is_constant + PARAMS ((const opname_map_e *)); +static unsigned op_get_constant + PARAMS ((const opname_map_e *)); +static size_t insn_templ_operand_count + PARAMS ((const insn_templ *)); + +/* parsing helpers. */ + +static const char *skip_white + PARAMS ((const char *)); +static void trim_whitespace + PARAMS ((char *)); +static void split_string + PARAMS ((split_rec *, const char *, char, bfd_boolean)); + +/* Language parsing. */ + +static bfd_boolean parse_insn_pattern + PARAMS ((const char *, insn_pattern *)); +static bfd_boolean parse_insn_repl + PARAMS ((const char *, insn_repl *)); +static bfd_boolean parse_insn_templ + PARAMS ((const char *, insn_templ *)); +static bfd_boolean parse_special_fn + PARAMS ((const char *, const char **, const char **)); +static bfd_boolean parse_precond + PARAMS ((const char *, precond_e *)); +static bfd_boolean parse_constant + PARAMS ((const char *, unsigned *)); +static bfd_boolean parse_id_constant + PARAMS ((const char *, const char *, unsigned *)); + +/* Transition table building code. */ + +static TransitionRule *build_transition + PARAMS ((insn_pattern *, insn_repl *, const char *, const char *)); +static TransitionTable *build_transition_table + PARAMS ((const string_pattern_pair *, size_t)); + + +void +append_transition (tt, opcode, t) + TransitionTable *tt; + xtensa_opcode opcode; + TransitionRule *t; +{ + TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList)); + TransitionList *prev; + TransitionList *nxt; + assert (tt != NULL); + assert (opcode < tt->num_opcodes); + + prev = tt->table[opcode]; + tl->rule = t; + tl->next = NULL; + if (prev == NULL) + { + tt->table[opcode] = tl; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = tl; + return; +} + + +void +append_condition (tr, cond) + TransitionRule *tr; + Precondition *cond; +{ + PreconditionList *pl = + (PreconditionList *) xmalloc (sizeof (PreconditionList)); + PreconditionList *prev = tr->conditions; + PreconditionList *nxt; + + pl->precond = cond; + pl->next = NULL; + if (prev == NULL) + { + tr->conditions = pl; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = pl; +} + + +void +append_value_condition (tr, cmp, op1, op2) + TransitionRule *tr; + CmpOp cmp; + unsigned op1; + unsigned op2; +{ + Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition)); + + cond->cmp = cmp; + cond->op_num = op1; + cond->typ = OP_OPERAND; + cond->op_data = op2; + append_condition (tr, cond); +} + + +void +append_constant_value_condition (tr, cmp, op1, cnst) + TransitionRule *tr; + CmpOp cmp; + unsigned op1; + unsigned cnst; +{ + Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition)); + + cond->cmp = cmp; + cond->op_num = op1; + cond->typ = OP_CONSTANT; + cond->op_data = cnst; + append_condition (tr, cond); +} + + +void +append_build_insn (tr, bi) + TransitionRule *tr; + BuildInstr *bi; +{ + BuildInstr *prev = tr->to_instr; + BuildInstr *nxt; + + bi->next = NULL; + if (prev == NULL) + { + tr->to_instr = bi; + return; + } + nxt = prev->next; + while (nxt != 0) + { + prev = nxt; + nxt = prev->next; + } + prev->next = bi; +} + + +void +append_op (bi, b_op) + BuildInstr *bi; + BuildOp *b_op; +{ + BuildOp *prev = bi->ops; + BuildOp *nxt; + + if (prev == NULL) + { + bi->ops = b_op; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = b_op; +} + + +void +append_literal_op (bi, op1, litnum) + BuildInstr *bi; + unsigned op1; + unsigned litnum; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_LITERAL; + b_op->op_data = litnum; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_label_op (bi, op1, labnum) + BuildInstr *bi; + unsigned op1; + unsigned labnum; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_LABEL; + b_op->op_data = labnum; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_constant_op (bi, op1, cnst) + BuildInstr *bi; + unsigned op1; + unsigned cnst; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_CONSTANT; + b_op->op_data = cnst; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_field_op (bi, op1, src_op) + BuildInstr *bi; + unsigned op1; + unsigned src_op; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_OPERAND; + b_op->op_data = src_op; + b_op->next = NULL; + append_op (bi, b_op); +} + + +/* These could be generated but are not currently. */ + +void +append_user_fn_field_op (bi, op1, typ, src_op) + BuildInstr *bi; + unsigned op1; + OpType typ; + unsigned src_op; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = typ; + b_op->op_data = src_op; + b_op->next = NULL; + append_op (bi, b_op); +} + + +/* These operand functions are the semantics of user-defined + operand functions. */ + +long +operand_function_HI24S (a) + long a; +{ + if (a & 0x80) + return (a & (~0xff)) + 0x100; + else + return (a & (~0xff)); +} + + +long +operand_function_F32MINUS (a) + long a; +{ + return (32 - a); +} + + +long +operand_function_LOW8 (a) + long a; +{ + if (a & 0x80) + return (a & 0xff) | ~0xff; + else + return (a & 0xff); +} + + +bfd_boolean +xg_has_userdef_op_fn (op) + OpType op; +{ + switch (op) + { + case OP_OPERAND_F32MINUS: + case OP_OPERAND_LOW8: + case OP_OPERAND_HI24S: + return TRUE; + default: + break; + } + return FALSE; +} + + +long +xg_apply_userdef_op_fn (op, a) + OpType op; + long a; +{ + switch (op) + { + case OP_OPERAND_F32MINUS: + return operand_function_F32MINUS (a); + case OP_OPERAND_LOW8: + return operand_function_LOW8 (a); + case OP_OPERAND_HI24S: + return operand_function_HI24S (a); + default: + break; + } + return FALSE; +} + + +/* Generate a transition table. */ + +const char * +enter_opname_n (name, len) + const char *name; + size_t len; +{ + opname_e *op; + + for (op = local_opnames; op != NULL; op = op->next) + { + if (strlen (op->opname) == len && strncmp (op->opname, name, len) == 0) + return op->opname; + } + op = (opname_e *) xmalloc (sizeof (opname_e)); + op->opname = (char *) xmalloc (len + 1); + strncpy (op->opname, name, len); + op->opname[len] = '\0'; + return op->opname; +} + + +static const char * +enter_opname (name) + const char *name; +{ + opname_e *op; + + for (op = local_opnames; op != NULL; op = op->next) + { + if (strcmp (op->opname, name) == 0) + return op->opname; + } + op = (opname_e *) xmalloc (sizeof (opname_e)); + op->opname = strdup (name); + return op->opname; +} + + +void +init_opname_map (m) + opname_map *m; +{ + m->head = NULL; + m->tail = &m->head; +} + + +void +clear_opname_map (m) + opname_map *m; +{ + opname_map_e *e; + + while (m->head != NULL) + { + e = m->head; + m->head = e->next; + free (e); + } + m->tail = &m->head; +} + + +static bfd_boolean +same_operand_name (m1, m2) + const opname_map_e *m1; + const opname_map_e *m2; +{ + if (m1->operand_name == NULL || m1->operand_name == NULL) + return FALSE; + return (m1->operand_name == m2->operand_name); +} + + +opname_map_e * +get_opmatch (map, operand_name) + opname_map *map; + const char *operand_name; +{ + opname_map_e *m; + + for (m = map->head; m != NULL; m = m->next) + { + if (strcmp (m->operand_name, operand_name) == 0) + return m; + } + return NULL; +} + + +bfd_boolean +op_is_constant (m1) + const opname_map_e *m1; +{ + return (m1->operand_name == NULL); +} + + +static unsigned +op_get_constant (m1) + const opname_map_e *m1; +{ + assert (m1->operand_name == NULL); + return m1->constant_value; +} + + +void +init_precond_list (l) + precond_list *l; +{ + l->head = NULL; + l->tail = &l->head; +} + + +void +clear_precond_list (l) + precond_list *l; +{ + precond_e *e; + + while (l->head != NULL) + { + e = l->head; + l->head = e->next; + free (e); + } + l->tail = &l->head; +} + + +void +init_insn_templ (t) + insn_templ *t; +{ + t->opcode_name = NULL; + init_opname_map (&t->operand_map); +} + + +void +clear_insn_templ (t) + insn_templ *t; +{ + clear_opname_map (&t->operand_map); +} + + +void +init_insn_pattern (p) + insn_pattern *p; +{ + init_insn_templ (&p->t); + init_precond_list (&p->preconds); +} + + +void +clear_insn_pattern (p) + insn_pattern *p; +{ + clear_insn_templ (&p->t); + clear_precond_list (&p->preconds); +} + + +void +init_insn_repl (r) + insn_repl *r; +{ + r->head = NULL; + r->tail = &r->head; +} + + +void +clear_insn_repl (r) + insn_repl *r; +{ + insn_repl_e *e; + + while (r->head != NULL) + { + e = r->head; + r->head = e->next; + clear_insn_templ (&e->t); + } + r->tail = &r->head; +} + + +static size_t +insn_templ_operand_count (t) + const insn_templ *t; +{ + size_t i = 0; + const opname_map_e *op; + + for (op = t->operand_map.head; op != NULL; op = op->next, ++i) + ; + return i; +} + + +/* Convert a string to a number. E.G.: parse_constant("10", &num) */ + +bfd_boolean +parse_constant (in, val_p) + const char *in; + unsigned *val_p; +{ + unsigned val = 0; + const char *p; + + if (in == NULL) + return FALSE; + p = in; + + while (*p != '\0') + { + if (*p >= '0' && *p <= '9') + val = val * 10 + (*p - '0'); + else + return FALSE; + ++p; + } + *val_p = val; + return TRUE; +} + + +/* Match a pattern like "foo1" with + parse_id_constant("foo1", "foo", &num). + This may also be used to just match a number. */ + +bfd_boolean +parse_id_constant (in, name, val_p) + const char *in; + const char *name; + unsigned *val_p; +{ + unsigned namelen = 0; + const char *p; + + if (in == NULL) + return FALSE; + + if (name != NULL) + namelen = strlen (name); + + if (name != NULL && strncmp (in, name, namelen) != 0) + return FALSE; + + p = &in[namelen]; + return parse_constant (p, val_p); +} + + +static bfd_boolean +parse_special_fn (name, fn_name_p, arg_name_p) + const char *name; + const char **fn_name_p; + const char **arg_name_p; +{ + char *p_start; + const char *p_end; + + p_start = strchr (name, '('); + if (p_start == NULL) + return FALSE; + + p_end = strchr (p_start, ')'); + + if (p_end == NULL) + return FALSE; + + if (p_end[1] != '\0') + return FALSE; + + *fn_name_p = enter_opname_n (name, p_start - name); + *arg_name_p = enter_opname_n (p_start + 1, p_end - p_start - 1); + return TRUE; +} + + +const char * +skip_white (p) + const char *p; +{ + if (p == NULL) + return p; + while (*p == ' ') + ++p; + return p; +} + + +void +trim_whitespace (in) + char *in; +{ + char *last_white = NULL; + char *p = in; + + while (p && *p != '\0') + { + while (*p == ' ') + { + if (last_white == NULL) + last_white = p; + p++; + } + if (*p != '\0') + { + last_white = NULL; + p++; + } + } + if (last_white) + *last_white = '\0'; +} + + +/* Split a string into component strings where "c" is the + delimiter. Place the result in the split_rec. */ + +void +split_string (rec, in, c, elide_whitespace) + split_rec *rec; + const char *in; + char c; + bfd_boolean elide_whitespace; +{ + size_t cnt = 0; + size_t i; + const char *p = in; + + while (p != NULL && *p != '\0') + { + cnt++; + p = strchr (p, c); + if (p) + p++; + } + rec->count = cnt; + rec->vec = NULL; + + if (rec->count == 0) + return; + + rec->vec = (char **) xmalloc (sizeof (char *) * cnt); + for (i = 0; i < cnt; i++) + rec->vec[i] = 0; + + p = in; + for (i = 0; i < cnt; i++) + { + const char *q; + size_t len; + + q = p; + if (elide_whitespace) + q = skip_white (q); + + p = strchr (q, c); + if (p == NULL) + rec->vec[i] = strdup (q); + else + { + len = p - q; + rec->vec[i] = (char *) xmalloc (sizeof (char) * (len + 1)); + strncpy (rec->vec[i], q, len); + rec->vec[i][len] = '\0'; + p++; + } + + if (elide_whitespace) + trim_whitespace (rec->vec[i]); + } +} + + +void +clear_split_rec (rec) + split_rec *rec; +{ + size_t i; + + for (i = 0; i < rec->count; ++i) + free (rec->vec[i]); + + if (rec->count > 0) + free (rec->vec); +} + + +void +init_split_rec (rec) + split_rec *rec; +{ + rec->vec = NULL; + rec->count = 0; +} + + +/* Parse an instruction template like "insn op1, op2, op3". */ + +bfd_boolean +parse_insn_templ (s, t) + const char *s; + insn_templ *t; +{ + const char *p = s; + /* First find the first whitespace. */ + size_t insn_name_len; + split_rec oprec; + size_t i; + + init_split_rec (&oprec); + + p = skip_white (p); + insn_name_len = strcspn (s, " "); + if (insn_name_len == 0) + return FALSE; + + init_insn_templ (t); + t->opcode_name = enter_opname_n (p, insn_name_len); + + p = p + insn_name_len; + + /* Split by ',' and skip beginning and trailing whitespace. */ + split_string (&oprec, p, ',', TRUE); + + for (i = 0; i < oprec.count; i++) + { + const char *opname = oprec.vec[i]; + opname_map_e *e = (opname_map_e *) xmalloc (sizeof (opname_map_e)); + e->next = NULL; + e->operand_name = NULL; + e->constant_value = 0; + e->operand_num = i; + + /* If it begins with a number, assume that it is a number. */ + if (opname && opname[0] >= '0' && opname[0] <= '9') + { + unsigned val; + + if (parse_constant (opname, &val)) + e->constant_value = val; + else + { + free (e); + clear_split_rec (&oprec); + clear_insn_templ (t); + return FALSE; + } + } + else + e->operand_name = enter_opname (oprec.vec[i]); + + *t->operand_map.tail = e; + t->operand_map.tail = &e->next; + } + clear_split_rec (&oprec); + return TRUE; +} + + +bfd_boolean +parse_precond (s, precond) + const char *s; + precond_e *precond; +{ + /* All preconditions are currently of the form: + a == b or a != b or a == k (where k is a constant). + Later we may use some special functions like DENSITY == 1 + to identify when density is available. */ + + const char *p = s; + size_t len; + precond->opname1 = NULL; + precond->opval1 = 0; + precond->cmpop = OP_EQUAL; + precond->opname2 = NULL; + precond->opval2 = 0; + precond->next = NULL; + + p = skip_white (p); + + len = strcspn (p, " !="); + + if (len == 0) + return FALSE; + + precond->opname1 = enter_opname_n (p, len); + p = p + len; + p = skip_white (p); + + /* Check for "==" and "!=". */ + if (strncmp (p, "==", 2) == 0) + precond->cmpop = OP_EQUAL; + else if (strncmp (p, "!=", 2) == 0) + precond->cmpop = OP_NOTEQUAL; + else + return FALSE; + + p = p + 2; + p = skip_white (p); + + /* No trailing whitespace from earlier parsing. */ + if (p[0] >= '0' && p[0] <= '9') + { + unsigned val; + if (parse_constant (p, &val)) + precond->opval2 = val; + else + return FALSE; + } + else + precond->opname2 = enter_opname (p); + return TRUE; +} + + +/* Parse a string like: + "insn op1, op2, op3, op4 | op1 != op2 | op2 == op3 | op4 == 1". + I.E., instruction "insn" with 4 operands where operand 1 and 2 are not + the same and operand 2 and 3 are the same and operand 4 is 1. */ + +bfd_boolean +parse_insn_pattern (in, insn) + const char *in; + insn_pattern *insn; +{ + + split_rec rec; + size_t i; + + init_split_rec (&rec); + init_insn_pattern (insn); + + split_string (&rec, in, '|', TRUE); + + if (rec.count == 0) + { + clear_split_rec (&rec); + return FALSE; + } + + if (!parse_insn_templ (rec.vec[0], &insn->t)) + { + clear_split_rec (&rec); + return FALSE; + } + + for (i = 1; i < rec.count; i++) + { + precond_e *cond = (precond_e *) xmalloc (sizeof (precond_e)); + + if (!parse_precond (rec.vec[i], cond)) + { + clear_split_rec (&rec); + clear_insn_pattern (insn); + return FALSE; + } + + /* Append the condition. */ + *insn->preconds.tail = cond; + insn->preconds.tail = &cond->next; + } + + clear_split_rec (&rec); + return TRUE; +} + + +bfd_boolean +parse_insn_repl (in, r_p) + const char *in; + insn_repl *r_p; +{ + /* This is a list of instruction templates separated by ';'. */ + split_rec rec; + size_t i; + + split_string (&rec, in, ';', TRUE); + + for (i = 0; i < rec.count; i++) + { + insn_repl_e *e = (insn_repl_e *) xmalloc (sizeof (insn_repl_e)); + + e->next = NULL; + + if (!parse_insn_templ (rec.vec[i], &e->t)) + { + free (e); + clear_insn_repl (r_p); + return FALSE; + } + *r_p->tail = e; + r_p->tail = &e->next; + } + return TRUE; +} + + +TransitionRule * +build_transition (initial_insn, replace_insns, from_string, to_string) + insn_pattern *initial_insn; + insn_repl *replace_insns; + const char *from_string; + const char *to_string; +{ + TransitionRule *tr = NULL; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + + opname_map_e *op1; + opname_map_e *op2; + + precond_e *precond; + insn_repl_e *r; + unsigned label_count = 0; + unsigned max_label_count = 0; + bfd_boolean has_label = FALSE; + unsigned literal_count = 0; + + opcode = xtensa_opcode_lookup (isa, initial_insn->t.opcode_name); + if (opcode == XTENSA_UNDEFINED) + { + /* It is OK to not be able to translate some of these opcodes. */ +#if 0 + as_warn (_("Invalid opcode '%s' in transition rule '%s'\n"), + initial_insn->t.opcode_name, to_string); +#endif + return NULL; + } + + + if (xtensa_num_operands (isa, opcode) + != (int) insn_templ_operand_count (&initial_insn->t)) + { + /* This is also OK because there are opcodes that + have different numbers of operands on different + architecture variations. */ +#if 0 + as_fatal (_("opcode %s mismatched operand count %d != expected %d"), + xtensa_opcode_name (isa, opcode), + xtensa_num_operands (isa, opcode), + insn_templ_operand_count (&initial_insn->t)); +#endif + return NULL; + } + + tr = (TransitionRule *) xmalloc (sizeof (TransitionRule)); + tr->opcode = opcode; + tr->conditions = NULL; + tr->to_instr = NULL; + + /* Build the conditions. First, equivalent operand condition.... */ + for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next) + { + for (op2 = op1->next; op2 != NULL; op2 = op2->next) + { + if (same_operand_name (op1, op2)) + { + append_value_condition (tr, OP_EQUAL, + op1->operand_num, op2->operand_num); + } + } + } + + /* Now the condition that an operand value must be a constant.... */ + for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next) + { + if (op_is_constant (op1)) + { + append_constant_value_condition (tr, + OP_EQUAL, + op1->operand_num, + op_get_constant (op1)); + } + } + + + /* Now add the explicit preconditions listed after the "|" in the spec. + These are currently very limited, so we do a special case + parse for them. We expect spaces, opname != opname. */ + for (precond = initial_insn->preconds.head; + precond != NULL; + precond = precond->next) + { + op1 = NULL; + op2 = NULL; + + if (precond->opname1) + { + op1 = get_opmatch (&initial_insn->t.operand_map, precond->opname1); + if (op1 == NULL) + { + as_fatal (_("opcode '%s': no bound opname '%s' " + "for precondition in '%s'"), + xtensa_opcode_name (isa, opcode), + precond->opname1, from_string); + return NULL; + } + } + + if (precond->opname2) + { + op2 = get_opmatch (&initial_insn->t.operand_map, precond->opname2); + if (op2 == NULL) + { + as_fatal (_("opcode '%s': no bound opname '%s' " + "for precondition in %s"), + xtensa_opcode_name (isa, opcode), + precond->opname2, from_string); + return NULL; + } + } + + if (op1 == NULL && op2 == NULL) + { + as_fatal (_("opcode '%s': precondition only contains " + "constants in '%s'"), + xtensa_opcode_name (isa, opcode), from_string); + return NULL; + } + else if (op1 != NULL && op2 != NULL) + append_value_condition (tr, precond->cmpop, + op1->operand_num, op2->operand_num); + else if (op2 == NULL) + append_constant_value_condition (tr, precond->cmpop, + op1->operand_num, precond->opval1); + else + append_constant_value_condition (tr, precond->cmpop, + op2->operand_num, precond->opval2); + } + + /* Generate the replacement instructions. Some of these + "instructions" are actually labels and literals. The literals + must be defined in order 0..n and a literal must be defined + (e.g., "LITERAL0 %imm") before use (e.g., "%LITERAL0"). The + labels must be defined in order, but they can be used before they + are defined. Also there are a number of special operands (e.g., + HI24S). */ + + for (r = replace_insns->head; r != NULL; r = r->next) + { + BuildInstr *bi; + const char *opcode_name; + size_t operand_count; + opname_map_e *op; + unsigned idnum = 0; + const char *fn_name; + const char *operand_arg_name; + + bi = (BuildInstr *) xmalloc (sizeof (BuildInstr)); + append_build_insn (tr, bi); + + bi->id = 0; + bi->opcode = XTENSA_UNDEFINED; + bi->ops = NULL; + bi->next = NULL; + + opcode_name = r->t.opcode_name; + operand_count = insn_templ_operand_count (&r->t); + + if (parse_id_constant (opcode_name, "LITERAL", &idnum)) + { + bi->typ = INSTR_LITERAL_DEF; + bi->id = idnum; + if (idnum != literal_count) + as_fatal (_("generated literals must be numbered consecutively")); + ++literal_count; + if (operand_count != 1) + as_fatal (_("expected one operand for generated literal")); + + } + else if (parse_id_constant (opcode_name, "LABEL", &idnum)) + { + bi->typ = INSTR_LABEL_DEF; + bi->id = idnum; + if (idnum != label_count) + as_fatal (_("generated labels must be numbered consecutively")); + ++label_count; + if (operand_count != 0) + as_fatal (_("expected 0 operands for generated label")); + } + else + { + bi->typ = INSTR_INSTR; + bi->opcode = xtensa_opcode_lookup (isa, r->t.opcode_name); + if (bi->opcode == XTENSA_UNDEFINED) + return NULL; + /* Check for the right number of ops. */ + if (xtensa_num_operands (isa, bi->opcode) + != (int) operand_count) + as_fatal (_("opcode '%s': replacement does not have %d ops"), + opcode_name, xtensa_num_operands (isa, bi->opcode)); + } + + for (op = r->t.operand_map.head; op != NULL; op = op->next) + { + unsigned idnum; + + if (op_is_constant (op)) + append_constant_op (bi, op->operand_num, op_get_constant (op)); + else if (parse_id_constant (op->operand_name, "%LITERAL", &idnum)) + { + if (idnum >= literal_count) + as_fatal (_("opcode %s: replacement " + "literal %d >= literal_count(%d)"), + opcode_name, idnum, literal_count); + append_literal_op (bi, op->operand_num, idnum); + } + else if (parse_id_constant (op->operand_name, "%LABEL", &idnum)) + { + has_label = TRUE; + if (idnum > max_label_count) + max_label_count = idnum; + append_label_op (bi, op->operand_num, idnum); + } + else if (parse_id_constant (op->operand_name, "a", &idnum)) + append_constant_op (bi, op->operand_num, idnum); + else if (op->operand_name[0] == '%') + { + opname_map_e *orig_op; + orig_op = get_opmatch (&initial_insn->t.operand_map, + op->operand_name); + if (orig_op == NULL) + { + as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + + append_constant_op (bi, op->operand_num, 0); + } + else + append_field_op (bi, op->operand_num, orig_op->operand_num); + } + else if (parse_special_fn (op->operand_name, + &fn_name, &operand_arg_name)) + { + opname_map_e *orig_op; + OpType typ = OP_CONSTANT; + + if (strcmp (fn_name, "LOW8") == 0) + typ = OP_OPERAND_LOW8; + else if (strcmp (fn_name, "HI24S") == 0) + typ = OP_OPERAND_HI24S; + else if (strcmp (fn_name, "F32MINUS") == 0) + typ = OP_OPERAND_F32MINUS; + else + as_fatal (_("unknown user defined function %s"), fn_name); + + orig_op = get_opmatch (&initial_insn->t.operand_map, + operand_arg_name); + if (orig_op == NULL) + { + as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + append_constant_op (bi, op->operand_num, 0); + } + else + append_user_fn_field_op (bi, op->operand_num, + typ, orig_op->operand_num); + } + else + { + as_fatal (_("opcode %s: could not parse operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + append_constant_op (bi, op->operand_num, 0); + } + } + } + if (has_label && max_label_count >= label_count) + { + as_fatal (_("opcode %s: replacement label %d >= label_count(%d)"), + xtensa_opcode_name (isa, opcode), + max_label_count, label_count); + return NULL; + } + + return tr; +} + + +TransitionTable * +build_transition_table (transitions, transition_count) + const string_pattern_pair *transitions; + size_t transition_count; +{ + TransitionTable *table = NULL; + int num_opcodes = xtensa_num_opcodes (xtensa_default_isa); + int i; + size_t tnum; + + if (table != NULL) + return table; + + /* Otherwise, build it now. */ + table = (TransitionTable *) xmalloc (sizeof (TransitionTable)); + table->num_opcodes = num_opcodes; + table->table = + (TransitionList **) xmalloc (sizeof (TransitionTable *) * num_opcodes); + + for (i = 0; i < num_opcodes; i++) + table->table[i] = NULL; + + for (tnum = 0; tnum < transition_count; tnum++) + { + const char *from_string = transitions[tnum].pattern; + const char *to_string = transitions[tnum].replacement; + + insn_pattern initial_insn; + insn_repl replace_insns; + TransitionRule *tr; + + init_insn_pattern (&initial_insn); + if (!parse_insn_pattern (from_string, &initial_insn)) + { + as_fatal (_("could not parse INSN_PATTERN '%s'"), from_string); + clear_insn_pattern (&initial_insn); + continue; + } + + init_insn_repl (&replace_insns); + if (!parse_insn_repl (to_string, &replace_insns)) + { + as_fatal (_("could not parse INSN_REPL '%s'"), to_string); + clear_insn_pattern (&initial_insn); + clear_insn_repl (&replace_insns); + continue; + } + + tr = build_transition (&initial_insn, &replace_insns, + from_string, to_string); + if (tr) + append_transition (table, tr->opcode, tr); + + clear_insn_repl (&replace_insns); + clear_insn_pattern (&initial_insn); + } + return table; +} + + +extern TransitionTable * +xg_build_widen_table () +{ + static TransitionTable *table = NULL; + if (table == NULL) + table = build_transition_table (widen_spec_list, WIDEN_COUNT); + return table; +} + + +extern TransitionTable * +xg_build_simplify_table () +{ + static TransitionTable *table = NULL; + if (table == NULL) + table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT); + return table; +} |