summaryrefslogtreecommitdiffstats
path: root/gas/config/xtensa-relax.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/xtensa-relax.c')
-rw-r--r--gas/config/xtensa-relax.c614
1 files changed, 480 insertions, 134 deletions
diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
index 49a93b20e0..4d2d01ac28 100644
--- a/gas/config/xtensa-relax.c
+++ b/gas/config/xtensa-relax.c
@@ -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.
@@ -23,20 +23,28 @@
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 )*
@@ -47,6 +55,13 @@
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
@@ -84,11 +99,11 @@
#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
@@ -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));
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,13 +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;
+ (*t_p) = tl;
}
@@ -759,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;
@@ -768,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;
@@ -789,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;
}
@@ -801,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));
@@ -830,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;
}
@@ -952,6 +1055,7 @@ init_insn_pattern (p)
{
init_insn_templ (&p->t);
init_precond_list (&p->preconds);
+ p->options = NULL;
}
@@ -989,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;
}
@@ -1131,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')
@@ -1156,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)
@@ -1164,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;
@@ -1184,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)
@@ -1194,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;
@@ -1211,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);
@@ -1277,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;
@@ -1322,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;
}
@@ -1360,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;
}
@@ -1369,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;
}
@@ -1381,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);
@@ -1404,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;
@@ -1430,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
@@ -1531,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
@@ -1549,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;
@@ -1592,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)
@@ -1650,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);
@@ -1686,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;
@@ -1733,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);
@@ -1746,20 +2090,22 @@ build_transition_table (transitions, transition_count)
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;
}
OpenPOWER on IntegriCloud