bfd ChangeLog
[deliverable/binutils-gdb.git] / gas / config / xtensa-relax.c
index 47aa03c60ac319050ec0d6a70027a73366a46c7c..4d2d01ac28f6e4bea9d55302bad7c2c8712c85c1 100644 (file)
@@ -1,5 +1,5 @@
 /* Table of relaxations for Xtensa assembly.
-   Copyright 2003 Free Software Foundation, Inc.
+   Copyright 2003, 2004 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    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.  
+   same or an operand is a specific constant or register.  The expansion
+   uses the bound variables from the pattern to specify that specific
+   operands from the pattern should be used in the result.  
+
+   The code determines whether the condition applies to a constant or
+   a register depending on the type of the operand.  You may get
+   unexpected results if you don't match the rule against the operand
+   type correctly.
 
    The patterns match a language like:
 
-   INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )*
+   INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )* ( '?' OPTIONPRED )*
    INSN_TEMPL   ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ]
    OPCODE       ::=  id
    OPERAND      ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')'
    SPECIALFN    ::= 'HI24S' | 'F32MINUS' | 'LOW8'
+                    | 'HI16' | 'LOW16'
    VARIABLE     ::= '%' id
    PRECOND      ::= OPERAND CMPOP OPERAND
    CMPOP        ::= '==' | '!='
+   OPTIONPRED   ::= OPTIONNAME ('+' OPTIONNAME)
+   OPTIONNAME   ::= '"' id '"'
 
    The replacement language 
    INSN_REPL      ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )*
    The operands in a PRECOND must be constants or variables bound by
    the INSN_PATTERN.
 
+   The configuration options define a predicate on the availability of
+   options which must be TRUE for this rule to be valid.  Examples are
+   requiring "density" for replacements with density instructions,
+   requiring "const16" for replacements that require const16
+   instructions, etc.  The names are interpreted by the assembler to a
+   truth value for a particular frag.
+
    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
 #include "xtensa-isa.h"
 #include "xtensa-relax.h"
 #include <stddef.h>
+#include "xtensa-config.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
@@ -115,7 +130,7 @@ 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;
+  int operand_num;
   unsigned constant_value;
   opname_map_e *next;
 };
@@ -173,6 +188,7 @@ struct insn_pattern_struct
 {
   insn_templ t;
   precond_list preconds;
+  ReqOptionList *options;
 };
 
 
@@ -202,7 +218,7 @@ typedef struct split_rec_struct split_rec;
 struct split_rec_struct
 {
   char **vec;
-  size_t count;
+  int count;
 };
 
 /* The "string_pattern_pair" is a set of pairs containing instruction
@@ -219,7 +235,7 @@ struct string_pattern_pair_struct
 /* 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
+   encodings.  A valid transition may require multiple 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'.
@@ -231,93 +247,113 @@ struct string_pattern_pair_struct
 
 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"},
+  {"add.n %ar,%as,%at ? IsaUseDensityInstruction", "add %ar,%as,%at"},
+  {"addi.n %ar,%as,%imm ? IsaUseDensityInstruction", "addi %ar,%as,%imm"},
+  {"beqz.n %as,%label ? IsaUseDensityInstruction", "beqz %as,%label"},
+  {"bnez.n %as,%label ? IsaUseDensityInstruction", "bnez %as,%label"},
+  {"l32i.n %at,%as,%imm ? IsaUseDensityInstruction", "l32i %at,%as,%imm"},
+  {"mov.n %at,%as ? IsaUseDensityInstruction", "or %at,%as,%as"},
+  {"movi.n %as,%imm ? IsaUseDensityInstruction", "movi %as,%imm"},
+  {"nop.n ? IsaUseDensityInstruction ? realnop", "nop"},
+  {"nop.n ? IsaUseDensityInstruction ? no-realnop", "or 1,1,1"},
+  {"ret.n %as ? IsaUseDensityInstruction", "ret %as"},
+  {"retw.n %as ? IsaUseDensityInstruction", "retw %as"},
+  {"s32i.n %at,%as,%imm ? IsaUseDensityInstruction", "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"},
+
+  /* Widening with literals or const16.  */
+  {"movi %at,%imm ? IsaUseL32R ", 
+   "LITERAL0 %imm; l32r %at,%LITERAL0"},
+  {"movi %at,%imm ? IsaUseConst16", 
+   "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm)"},
+
   {"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",
+
+  /* In the end convert to either an l32r or const16.  */
+  {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseL32R",
    "LITERAL0 %imm; l32r %ar,%LITERAL0; add %ar,%as,%ar"},
+  {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseConst16",
+   "const16 %ar,HI16U(%imm); const16 %ar,LOW16U(%imm); add %ar,%as,%ar"},
 
   /* Widening the load instructions with too-large immediates */
-  {"l8ui %at,%as,%imm | %at!=%as",
+  {"l8ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
    "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l8ui %at,%at,0"},
-  {"l16si %at,%as,%imm | %at!=%as",
+  {"l16si %at,%as,%imm | %at!=%as ? IsaUseL32R",
    "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16si %at,%at,0"},
-  {"l16ui %at,%as,%imm | %at!=%as",
+  {"l16ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
    "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16ui %at,%at,0"},
+  {"l32i %at,%as,%imm | %at!=%as ? IsaUseL32R",
+   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"},
+
+  /* Widening load instructions with const16s.  */
+  {"l8ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+   "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l8ui %at,%at,0"},
+  {"l16si %at,%as,%imm | %at!=%as ? IsaUseConst16",
+   "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16si %at,%at,0"},
+  {"l16ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+   "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16ui %at,%at,0"},
+  {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+   "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+
 #if 0 /* Xtensa Synchronization Option not yet available */
-  {"l32ai %at,%as,%imm",
+  {"l32ai %at,%as,%imm ? IsaUseL32R",
    "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",
+  {"l32is %at,%as,%imm ? IsaUseL32R",
    "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",
+  /* This is only PART of the loop instruction.  In addition,
+     hardcoded into its use is a modification of the final operand in
+     the instruction in bytes 9 and 12.  */
+  {"loop %as,%label | %as!=1 ? IsaUseLoops",
    "loop %as,%LABEL0;"
-   "rsr     %as, 1;"           /* LEND */
-   "wsr     %as, 0;"           /* LBEG */
+   "rsr.lend    %as;"          /* LEND */
+   "wsr.lbeg    %as;"          /* LBEG */
    "addi    %as, %as, 0;"      /* lo8(%label-%LABEL1) */
    "addmi   %as, %as, 0;"      /* mid8(%label-%LABEL1) */
-   "wsr     %as, 1;"
+   "wsr.lend    %as;"
    "isync;"
-   "rsr     %as, 2;"           /* LCOUNT */
+   "rsr.lcount    %as;"                /* LCOUNT */
    "addi    %as, %as, 1;"      /* density -> addi.n %as, %as, 1 */
    "LABEL0"},
-  {"loopgtz %as,%label",
-   "beqz     %as,%label;"
-   "bltz     %as,%label;"
+  {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
+   "beqz    %as,%label;"
+   "bltz    %as,%label;"
    "loopgtz %as,%LABEL0;"
-   "rsr     %as, 1;"           /* LEND */
-   "wsr     %as, 0;"           /* LBEG */
+   "rsr.lend    %as;"          /* LEND */
+   "wsr.lbeg    %as;"          /* LBEG */
    "addi    %as, %as, 0;"      /* lo8(%label-%LABEL1) */
    "addmi   %as, %as, 0;"      /* mid8(%label-%LABEL1) */
-   "wsr     %as, 1;"
+   "wsr.lend    %as;"
    "isync;"
-   "rsr     %as, 2;"           /* LCOUNT */
+   "rsr.lcount    %as;"                /* LCOUNT */
    "addi    %as, %as, 1;"      /* density -> addi.n %as, %as, 1 */
    "LABEL0"},
-  {"loopnez %as,%label",
+  {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
    "beqz     %as,%label;"
    "loopnez %as,%LABEL0;"
-   "rsr     %as, 1;"           /* LEND */
-   "wsr     %as, 0;"           /* LBEG */
+   "rsr.lend    %as;"          /* LEND */
+   "wsr.lbeg    %as;"          /* LBEG */
    "addi    %as, %as, 0;"      /* lo8(%label-%LABEL1) */
    "addmi   %as, %as, 0;"      /* mid8(%label-%LABEL1) */
-   "wsr     %as, 1;"
+   "wsr.lend    %as;"
    "isync;"
-   "rsr     %as, 2;"           /* LCOUNT */
+   "rsr.lcount    %as;"                /* 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 ? IsaUseDensityInstruction", "bnez.n %as,%LABEL0;j %label;LABEL0"},
+  {"bnez %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%LABEL0;j %label;LABEL0"},
   {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"},
   {"bnez %as,%label", "beqz %as,%LABEL0;j %label;LABEL0"},
-#endif
+  {"beqzt %as,%label ? IsaUsePredictedBranches", "bnez %as,%LABEL0;j %label;LABEL0"},
+  {"bnezt %as,%label ? IsaUsePredictedBranches", "beqz %as,%LABEL0;j %label;LABEL0"},
 
   {"bgez %as,%label", "bltz %as,%LABEL0;j %label;LABEL0"},
   {"bltz %as,%label", "bgez %as,%LABEL0;j %label;LABEL0"},
@@ -331,24 +367,42 @@ static string_pattern_pair widen_spec_list[] =
   {"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"},
+  {"beqt %as,%at,%label ? IsaUsePredictedBranches", "bne %as,%at,%LABEL0;j %label;LABEL0"},
+  {"bnet %as,%at,%label ? IsaUsePredictedBranches", "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
+
+  {"bt %bs,%label ? IsaUseBooleans", "bf %bs,%LABEL0;j %label;LABEL0"},
+  {"bf %bs,%label ? IsaUseBooleans", "bt %bs,%LABEL0;j %label;LABEL0"},
+
   {"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"}
+
+  /* Expanding calls with literals.  */
+  {"call0 %label,%ar0 ? IsaUseL32R",
+   "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0,%ar0"},
+  {"call4 %label,%ar4 ? IsaUseL32R",
+   "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4,%ar4"},
+  {"call8 %label,%ar8 ? IsaUseL32R",
+   "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8,%ar8"},
+  {"call12 %label,%ar12 ? IsaUseL32R",
+   "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12,%ar12"},
+
+  /* Expanding calls with const16.  */
+  {"call0 %label,%ar0 ? IsaUseConst16",
+   "const16 a0,HI16U(%label); const16 a0,LOW16U(%label); callx0 a0,%ar0"},
+  {"call4 %label,%ar4 ? IsaUseConst16",
+   "const16 a4,HI16U(%label); const16 a4,LOW16U(%label); callx4 a4,%ar4"},
+  {"call8 %label,%ar8 ? IsaUseConst16",
+   "const16 a8,HI16U(%label); const16 a8,LOW16U(%label); callx8 a8,%ar8"},
+  {"call12 %label,%ar12 ? IsaUseConst16",
+   "const16 a12,HI16U(%label); const16 a12,LOW16U(%label); callx12 a12,%ar12"}
 };
 
 #define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair))
@@ -366,20 +420,22 @@ static string_pattern_pair widen_spec_list[] =
 
 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"}
+  {"add %ar,%as,%at ? IsaUseDensityInstruction", "add.n %ar,%as,%at"},
+  {"addi.n %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+  {"addi %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+  {"addi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+  {"addmi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+  {"beqz %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%label"},
+  {"bnez %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%label"},
+  {"l32i %at,%as,%imm ? IsaUseDensityInstruction", "l32i.n %at,%as,%imm"},
+  {"movi %as,%imm ? IsaUseDensityInstruction", "movi.n %as,%imm"},
+  {"nop ? realnop ? IsaUseDensityInstruction", "nop.n"},
+  {"or %ar,%as,%at | %ar==%as | %as==%at ? IsaUseDensityInstruction", "nop.n"},
+  {"or %ar,%as,%at | %ar!=%as | %as==%at ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+  {"ret %as ? IsaUseDensityInstruction", "ret.n %as"},
+  {"retw %as ? IsaUseDensityInstruction", "retw.n %as"},
+  {"s32i %at,%as,%imm ? IsaUseDensityInstruction", "s32i.n %at,%as,%imm"},
+  {"slli %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"}
 };
 
 #define SIMPLIFY_COUNT \
@@ -389,7 +445,8 @@ string_pattern_pair simplify_spec_list[] =
 /* Transition generation helpers.  */
 
 static void append_transition 
-  PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *));
+  PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *,
+          transition_cmp_fn));
 static void append_condition 
   PARAMS ((TransitionRule *, Precondition *));
 static void append_value_condition 
@@ -416,6 +473,10 @@ static long operand_function_F32MINUS
   PARAMS ((long));
 static long operand_function_LOW8
   PARAMS ((long));
+static long operand_function_LOW16U
+  PARAMS ((long));
+static long operand_function_HI16U
+  PARAMS ((long));
 
 /* Externally visible functions.  */
 
@@ -427,7 +488,7 @@ extern long xg_apply_userdef_op_fn
 /* Parsing helpers.  */
 
 static const char *enter_opname_n
-  PARAMS ((const char *, size_t));
+  PARAMS ((const char *, int));
 static const char *enter_opname
   PARAMS ((const char *));
 
@@ -457,6 +518,14 @@ static void init_split_rec
   PARAMS ((split_rec *));
 static void clear_split_rec
   PARAMS ((split_rec *));
+static void clear_req_or_option_list
+  PARAMS ((ReqOrOption **));
+static void clear_req_option_list
+  PARAMS ((ReqOption **));
+static ReqOrOption *clone_req_or_option_list
+  PARAMS ((ReqOrOption *));
+static ReqOption *clone_req_option_list
+  PARAMS ((ReqOption *));
 
 /* Operand and insn_templ helpers.  */
 
@@ -468,10 +537,10 @@ 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
+static int insn_templ_operand_count
   PARAMS ((const insn_templ *));
 
-/* parsing helpers.  */
+/* Parsing helpers.  */
 
 static const char *skip_white
   PARAMS ((const char *));
@@ -496,24 +565,29 @@ static bfd_boolean parse_constant
   PARAMS ((const char *, unsigned *));
 static bfd_boolean parse_id_constant 
   PARAMS ((const char *, const char *, unsigned *));
+static bfd_boolean parse_option_cond
+  PARAMS ((const char *, ReqOption *));
 
 /* Transition table building code.  */
 
+static bfd_boolean transition_applies 
+  PARAMS ((insn_pattern *, const char *, const char *));
 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));
+  PARAMS ((const string_pattern_pair *, int, transition_cmp_fn));
 
 \f
 void
-append_transition (tt, opcode, t)
+append_transition (tt, opcode, t, cmp)
      TransitionTable *tt;
      xtensa_opcode opcode;
      TransitionRule *t;
+     transition_cmp_fn cmp;
 {
   TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList));
   TransitionList *prev;
-  TransitionList *nxt;
+  TransitionList **t_p;
   assert (tt != NULL);
   assert (opcode < tt->num_opcodes);
 
@@ -525,14 +599,18 @@ append_transition (tt, opcode, t)
       tt->table[opcode] = tl;
       return;
     }
-  nxt = prev->next;
-  while (nxt != NULL)
+
+  for (t_p = &tt->table[opcode]; (*t_p) != NULL; t_p = &(*t_p)->next)
     {
-      prev = nxt;
-      nxt = nxt->next;
+      if (cmp && cmp (t, (*t_p)->rule) < 0)
+       {
+         /* Insert it here.  */
+         tl->next = *t_p;
+         *t_p = tl;
+         return;
+       }
     }
-  prev->next = tl;
-  return;
+  (*t_p) = tl;
 }
 
 
@@ -760,6 +838,23 @@ operand_function_LOW8 (a)
 }
 
 
+long
+operand_function_LOW16U (a)
+     long a;
+{
+  return (a & 0xffff);
+}
+
+
+long
+operand_function_HI16U (a)
+     long a;
+{
+  unsigned long b = a & 0xffff0000;
+  return (long) (b >> 16);
+}
+
+
 bfd_boolean
 xg_has_userdef_op_fn (op)
      OpType op;
@@ -769,6 +864,8 @@ xg_has_userdef_op_fn (op)
     case OP_OPERAND_F32MINUS:
     case OP_OPERAND_LOW8:
     case OP_OPERAND_HI24S:
+    case OP_OPERAND_LOW16U:
+    case OP_OPERAND_HI16U:
       return TRUE;
     default:
       break;
@@ -790,6 +887,10 @@ xg_apply_userdef_op_fn (op, a)
       return operand_function_LOW8 (a);
     case OP_OPERAND_HI24S:
       return operand_function_HI24S (a);
+    case OP_OPERAND_LOW16U:
+      return operand_function_LOW16U (a);
+    case OP_OPERAND_HI16U:
+      return operand_function_HI16U (a);
     default:
       break;
     }
@@ -802,13 +903,14 @@ xg_apply_userdef_op_fn (op, a)
 const char *
 enter_opname_n (name, len)
      const char *name;
-     size_t len;
+     int len;
 {
   opname_e *op;
 
   for (op = local_opnames; op != NULL; op = op->next)
     {
-      if (strlen (op->opname) == len && strncmp (op->opname, name, len) == 0)
+      if (strlen (op->opname) == (unsigned) len
+         && strncmp (op->opname, name, len) == 0)
        return op->opname;
     }
   op = (opname_e *) xmalloc (sizeof (opname_e));
@@ -831,7 +933,7 @@ enter_opname (name)
        return op->opname;
     }
   op = (opname_e *) xmalloc (sizeof (opname_e));
-  op->opname = strdup (name);
+  op->opname = xstrdup (name);
   return op->opname;
 }
 
@@ -953,6 +1055,7 @@ init_insn_pattern (p)
 {
   init_insn_templ (&p->t);
   init_precond_list (&p->preconds);
+  p->options = NULL;
 }
 
 
@@ -990,14 +1093,14 @@ clear_insn_repl (r)
 }
 
 
-static size_t
+static int
 insn_templ_operand_count (t)
      const insn_templ *t;
 {
-  size_t i = 0;
+  int i = 0;
   const opname_map_e *op;
 
-  for (op = t->operand_map.head; op != NULL; op = op->next, ++i)
+  for (op = t->operand_map.head; op != NULL; op = op->next, i++)
     ;
   return i;
 }
@@ -1132,8 +1235,8 @@ split_string (rec, in, c, elide_whitespace)
      char c;
      bfd_boolean elide_whitespace;
 {
-  size_t cnt = 0;
-  size_t i;
+  int cnt = 0;
+  int i;
   const char *p = in;
 
   while (p != NULL && *p != '\0')
@@ -1157,7 +1260,7 @@ split_string (rec, in, c, elide_whitespace)
   for (i = 0; i < cnt; i++)
     {
       const char *q;
-      size_t len;
+      int len;
 
       q = p;
       if (elide_whitespace)
@@ -1165,7 +1268,7 @@ split_string (rec, in, c, elide_whitespace)
 
       p = strchr (q, c);
       if (p == NULL)
-       rec->vec[i] = strdup (q);
+       rec->vec[i] = xstrdup (q);
       else
        {
          len = p - q;
@@ -1185,9 +1288,9 @@ void
 clear_split_rec (rec)
      split_rec *rec;
 {
-  size_t i;
+  int i;
 
-  for (i = 0; i < rec->count; ++i)
+  for (i = 0; i < rec->count; i++)
     free (rec->vec[i]);
 
   if (rec->count > 0)
@@ -1195,6 +1298,9 @@ clear_split_rec (rec)
 }
 
 
+/* Initialize a split record.  The split record must be initialized
+   before split_string is called.  */
+
 void
 init_split_rec (rec)
      split_rec *rec;
@@ -1212,10 +1318,11 @@ parse_insn_templ (s, t)
      insn_templ *t;
 {
   const char *p = s;
-  /* First find the first whitespace.  */
-  size_t insn_name_len;
+  int insn_name_len;
   split_rec oprec;
-  size_t i;
+  int i;
+
+  /* First find the first whitespace.  */
 
   init_split_rec (&oprec);
 
@@ -1278,7 +1385,7 @@ parse_precond (s, precond)
      to identify when density is available.  */
 
   const char *p = s;
-  size_t len;
+  int len;
   precond->opname1 = NULL;
   precond->opval1 = 0;
   precond->cmpop = OP_EQUAL;
@@ -1323,34 +1430,170 @@ parse_precond (s, precond)
 }
 
 
+void
+clear_req_or_option_list (r_p)
+     ReqOrOption **r_p;
+{
+  if (*r_p == NULL)
+    return;
+
+  free ((*r_p)->option_name);
+  clear_req_or_option_list (&(*r_p)->next);
+  *r_p = NULL;
+}
+
+
+void
+clear_req_option_list (r_p)
+     ReqOption **r_p;
+{
+  if (*r_p == NULL)
+    return;
+
+  clear_req_or_option_list (&(*r_p)->or_option_terms);
+  clear_req_option_list (&(*r_p)->next);
+  *r_p = NULL;
+}
+
+
+ReqOrOption *
+clone_req_or_option_list (req_or_option)
+     ReqOrOption *req_or_option;
+{
+  ReqOrOption *new_req_or_option;
+
+  if (req_or_option == NULL)
+    return NULL;
+
+  new_req_or_option = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+  new_req_or_option->option_name = xstrdup (req_or_option->option_name);
+  new_req_or_option->is_true = req_or_option->is_true;
+  new_req_or_option->next = NULL;
+  new_req_or_option->next = clone_req_or_option_list (req_or_option->next);
+  return new_req_or_option;
+}
+
+
+ReqOption *
+clone_req_option_list (req_option)
+     ReqOption *req_option;
+{
+  ReqOption *new_req_option;
+
+  if (req_option == NULL)
+    return NULL;
+
+  new_req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+  new_req_option->or_option_terms = NULL;
+  new_req_option->next = NULL;
+  new_req_option->or_option_terms = 
+    clone_req_or_option_list (req_option->or_option_terms);
+  new_req_option->next = clone_req_option_list (req_option->next);
+  return new_req_option;
+}
+
+
+bfd_boolean
+parse_option_cond (s, option)
+     const char *s;
+     ReqOption *option;
+{
+  int i;
+  split_rec option_term_rec;
+
+  /* All option or conditions are of the form:
+     optionA + no-optionB + ...
+     "Ands" are divided by "?".  */
+
+  init_split_rec (&option_term_rec);
+  split_string (&option_term_rec, s, '+', TRUE);
+
+  if (option_term_rec.count == 0)
+    {
+      clear_split_rec (&option_term_rec);
+      return FALSE;
+    }
+
+  for (i = 0; i < option_term_rec.count; i++)
+    {
+      char *option_name = option_term_rec.vec[i];
+      bfd_boolean is_true = TRUE;
+      ReqOrOption *req;
+      ReqOrOption **r_p;
+
+      if (strncmp (option_name, "no-", 3) == 0)
+       {
+         option_name = xstrdup (&option_name[3]);
+         is_true = FALSE;
+       }
+      else
+       option_name = xstrdup (option_name);
+
+      req = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+      req->option_name = option_name;
+      req->is_true = is_true;
+      req->next = NULL;
+
+      /* Append to list.  */
+      for (r_p = &option->or_option_terms; (*r_p) != NULL; 
+          r_p = &(*r_p)->next)
+       ;
+      (*r_p) = req;
+    }
+  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.  */
+   the same and operand 2 and 3 are the same and operand 4 is 1.
+
+   or:
+
+   "insn op1 | op1 == 1 / density + boolean / no-useroption".
+   i.e. instruction "insn" with 1 operands where operand 1 is 1
+   when "density" or "boolean" options are available and
+   "useroption" is not available.
+
+   Because the current implementation of this parsing scheme uses
+   split_string, it requires that '|' and '?' are only used as
+   delimiters for predicates and required options.  */
 
 bfd_boolean
 parse_insn_pattern (in, insn)
      const char *in;
      insn_pattern *insn;
 {
-
   split_rec rec;
-  size_t i;
+  split_rec optionrec;
+  int i;
 
-  init_split_rec (&rec);
   init_insn_pattern (insn);
 
-  split_string (&rec, in, '|', TRUE);
+  init_split_rec (&optionrec);
+  split_string (&optionrec, in, '?', TRUE);
+  if (optionrec.count == 0)
+    {
+      clear_split_rec (&optionrec);
+      return FALSE;
+    }
+  
+  init_split_rec (&rec);
+
+  split_string (&rec, optionrec.vec[0], '|', TRUE);
 
   if (rec.count == 0)
     {
       clear_split_rec (&rec);
+      clear_split_rec (&optionrec);
       return FALSE;
     }
 
   if (!parse_insn_templ (rec.vec[0], &insn->t))
     {
       clear_split_rec (&rec);
+      clear_split_rec (&optionrec);
       return FALSE;
     }
 
@@ -1361,6 +1604,7 @@ parse_insn_pattern (in, insn)
       if (!parse_precond (rec.vec[i], cond))
        {
          clear_split_rec (&rec);
+         clear_split_rec (&optionrec);
          clear_insn_pattern (insn);
          return FALSE;
        }
@@ -1370,7 +1614,32 @@ parse_insn_pattern (in, insn)
       insn->preconds.tail = &cond->next;
     }
 
+  for (i = 1; i < optionrec.count; i++)
+    {
+      /* Handle the option conditions.  */
+      ReqOption **r_p;
+      ReqOption *req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+      req_option->or_option_terms = NULL;
+      req_option->next = NULL;
+      
+      if (!parse_option_cond (optionrec.vec[i], req_option))
+       {
+         clear_split_rec (&rec);
+         clear_split_rec (&optionrec);
+         clear_insn_pattern (insn);
+         clear_req_option_list (&req_option);
+         return FALSE;
+       }
+
+      /* Append the condition.  */
+      for (r_p = &insn->options; (*r_p) != NULL; r_p = &(*r_p)->next)
+       ;
+
+      (*r_p) = req_option;
+    }
+
   clear_split_rec (&rec);
+  clear_split_rec (&optionrec);
   return TRUE;
 }
 
@@ -1382,7 +1651,7 @@ parse_insn_repl (in, r_p)
 {
   /* This is a list of instruction templates separated by ';'.  */
   split_rec rec;
-  size_t i;
+  int i;
 
   split_string (&rec, in, ';', TRUE);
 
@@ -1405,6 +1674,59 @@ parse_insn_repl (in, r_p)
 }
 
 
+bfd_boolean
+transition_applies (initial_insn, from_string, to_string)
+     insn_pattern *initial_insn;
+     const char *from_string ATTRIBUTE_UNUSED; 
+     const char *to_string ATTRIBUTE_UNUSED; 
+{
+  ReqOption *req_option;
+
+  for (req_option = initial_insn->options;
+       req_option != NULL;
+       req_option = req_option->next)
+    {
+      ReqOrOption *req_or_option = req_option->or_option_terms;
+
+      if (req_or_option == NULL
+         || req_or_option->next != NULL)
+       continue;
+
+      if (strncmp (req_or_option->option_name, "IsaUse", 6) == 0) 
+       {
+         bfd_boolean option_available = FALSE;
+         char *option_name = req_or_option->option_name + 6;
+         if (!strcmp (option_name, "DensityInstruction"))
+           option_available = (XCHAL_HAVE_DENSITY == 1);
+         else if (!strcmp (option_name, "L32R"))
+           option_available = (XCHAL_HAVE_L32R == 1);
+         else if (!strcmp (option_name, "Const16"))
+           option_available = (XCHAL_HAVE_CONST16 == 1);
+         else if (!strcmp (option_name, "Loops"))
+           option_available = (XCHAL_HAVE_LOOPS == 1);
+         else if (!strcmp (option_name, "PredictedBranches"))
+           option_available = (XCHAL_HAVE_PREDICTED_BRANCHES == 1);
+         else if (!strcmp (option_name, "Booleans"))
+           option_available = (XCHAL_HAVE_BOOLEANS == 1);
+         else
+           as_warn (_("invalid configuration option '%s' in transition rule '%s'"),
+                    req_or_option->option_name, from_string);
+         if ((option_available ^ req_or_option->is_true) != 0)
+           return FALSE;
+       }
+      else if (strcmp (req_or_option->option_name, "realnop") == 0)
+       {
+         bfd_boolean nop_available = 
+           (xtensa_opcode_lookup (xtensa_default_isa, "nop")
+            != XTENSA_UNDEFINED);
+         if ((nop_available ^ req_or_option->is_true) != 0)
+           return FALSE;
+       }
+    }
+  return TRUE;
+}
+
+
 TransitionRule *
 build_transition (initial_insn, replace_insns, from_string, to_string)
      insn_pattern *initial_insn;
@@ -1431,15 +1753,15 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
     {
       /* 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);
+      as_warn (_("invalid opcode '%s' in transition rule '%s'"),
+              initial_insn->t.opcode_name, from_string);
 #endif
       return NULL;
     }
 
 
-  if (xtensa_num_operands (isa, opcode)
-      != (int) insn_templ_operand_count (&initial_insn->t))
+  if (xtensa_opcode_num_operands (isa, opcode)
+      != insn_templ_operand_count (&initial_insn->t))
     {
       /* This is also OK because there are opcodes that
         have different numbers of operands on different
@@ -1532,12 +1854,14 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
                                op1->operand_num, op2->operand_num);
       else if (op2 == NULL)
        append_constant_value_condition (tr, precond->cmpop,
-                                        op1->operand_num, precond->opval1);
+                                        op1->operand_num, precond->opval2);
       else
        append_constant_value_condition (tr, precond->cmpop,
-                                        op2->operand_num, precond->opval2);
+                                        op2->operand_num, precond->opval1);
     }
 
+  tr->options = clone_req_option_list (initial_insn->options);
+
   /* 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
@@ -1550,7 +1874,7 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
     {
       BuildInstr *bi;
       const char *opcode_name;
-      size_t operand_count;
+      int operand_count;
       opname_map_e *op;
       unsigned idnum = 0;
       const char *fn_name;
@@ -1593,12 +1917,17 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
          bi->typ = INSTR_INSTR;
          bi->opcode = xtensa_opcode_lookup (isa, r->t.opcode_name);
          if (bi->opcode == XTENSA_UNDEFINED)
-           return NULL;
+           {
+             as_warn (_("invalid opcode '%s' in transition rule '%s'"),
+                      r->t.opcode_name, to_string);
+             return NULL;
+           }
          /* Check for the right number of ops.  */
-         if (xtensa_num_operands (isa, bi->opcode) 
+         if (xtensa_opcode_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));
+                     opcode_name,
+                     xtensa_opcode_num_operands (isa, bi->opcode));
        }
 
       for (op = r->t.operand_map.head; op != NULL; op = op->next)
@@ -1651,8 +1980,12 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
                typ = OP_OPERAND_HI24S;
              else if (strcmp (fn_name, "F32MINUS") == 0)
                typ = OP_OPERAND_F32MINUS;
+             else if (strcmp (fn_name, "LOW16U") == 0)
+               typ = OP_OPERAND_LOW16U;
+             else if (strcmp (fn_name, "HI16U") == 0)
+               typ = OP_OPERAND_HI16U;
              else
-               as_fatal (_("unknown user defined function %s"), fn_name);
+               as_fatal (_("unknown user-defined function %s"), fn_name);
 
              orig_op = get_opmatch (&initial_insn->t.operand_map,
                                     operand_arg_name);
@@ -1687,14 +2020,14 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
 
 
 TransitionTable *
-build_transition_table (transitions, transition_count)
+build_transition_table (transitions, transition_count, cmp)
      const string_pattern_pair *transitions;
-     size_t transition_count;
+     int transition_count;
+     transition_cmp_fn cmp;
 {
   TransitionTable *table = NULL;
-  int num_opcodes = xtensa_num_opcodes (xtensa_default_isa);
-  int i;
-  size_t tnum;
+  int num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+  int i, tnum;
 
   if (table != NULL)
     return table;
@@ -1734,10 +2067,20 @@ build_transition_table (transitions, transition_count)
          continue;
        }
 
-      tr = build_transition (&initial_insn, &replace_insns,
-                            from_string, to_string);
-      if (tr)
-       append_transition (table, tr->opcode, tr);
+      if (transition_applies (&initial_insn, from_string, to_string))
+       {
+         tr = build_transition (&initial_insn, &replace_insns,
+                                from_string, to_string);
+         if (tr)
+           append_transition (table, tr->opcode, tr, cmp);
+         else
+           {
+#if TENSILICA_DEBUG
+             as_warn (_("could not build transition for %s => %s"),
+                      from_string, to_string);
+#endif
+           }
+       }
 
       clear_insn_repl (&replace_insns);
       clear_insn_pattern (&initial_insn);
@@ -1747,20 +2090,22 @@ build_transition_table (transitions, transition_count)
 
 \f
 extern TransitionTable *
-xg_build_widen_table ()
+xg_build_widen_table (cmp)
+     transition_cmp_fn cmp;
 {
   static TransitionTable *table = NULL;
   if (table == NULL)
-    table = build_transition_table (widen_spec_list, WIDEN_COUNT);
+    table = build_transition_table (widen_spec_list, WIDEN_COUNT, cmp);
   return table;
 }
 
 
 extern TransitionTable *
-xg_build_simplify_table ()
+xg_build_simplify_table (cmp)
+     transition_cmp_fn cmp;
 {
   static TransitionTable *table = NULL;
   if (table == NULL)
-    table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT);
+    table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT, cmp);
   return table;
 }
This page took 0.03966 seconds and 4 git commands to generate.