This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Automatized pattern matching
- From: Zdenek Dvorak <rakdver at atrey dot karlin dot mff dot cuni dot cz>
- To: gcc-patches at gcc dot gnu dot org
- Cc: zack at codesourcery dot com
- Date: Wed, 18 Jun 2003 23:40:12 +0200
- Subject: [PATCH] Automatized pattern matching
Hello,
I have polished the patch a bit, added documentation and implemented a
few features (most importantly it is possible now to split the pattern
to more than one line).
Bootstrapped and tested on i686.
Zdenek
Changelog:
* genpattern.c: New.
* doc/match.texi: New.
* Makefile.in (STAGESTUFF): Add genpattern, s-match and match-*.h files.
(genpattern, genpattern.o, s-match): Add rules.
(cse.o): Add match-cse.h dependency.
(MATCHFILES): New variable.
* cse.c: Include match-cse.h.
(fixed_base_plus_p, address_cost, mention_regs, canon_hash,
cse_rtx_varies_p, find_best_addr, find_comparison_args, fold_rtx,
cse_insn, addr_affects_sp_p, invalidate_from_clobbers,
cse_around_loop, invalidate_skipped_set, cse_set_around_loop,
check_for_label_ref, count_reg_usage): Use generated patterns.
genpattern.c:
=============
/* Pattern matching utilities.
Copyright (C) 2003 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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.
GCC 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 GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "bitmap.h"
/* This program is used to generate conditions for pattern matching of rtl
expressions. Usage:
The source file foo.c where you want to use the feature must include
match-foo.h. The file is generated by this program; to ensure it, you must
add it to Makefile.in. Then whenever you need to do a pattern matching,
just use MATCH_RTX (pattern, expression); this macro expands to a condition
that performs the pattern matching, binds the defined variables and returns
whether match succeeded (if it does not, contents of used variables is
undefined; otherwise it is set according to the leftmost succesful match).
At most one MATCH_RTX macro may be used on a single line (where the line
where the closing bracket of macro arguments is counted as containing the
macro).
Exact (almost; for easier reading some conditions on it are only mentioned
in comments) grammar of pattern expressions is
pattern = vexpression | vexpression / predicate /
-- if predicate is supplied (any text that does not
contain a /), value matched by vexpression is bound
to _loc variable and predicate is requiered to be
true. If predicate does not contain ( character,
the location is passed to it as argument instead
pexpression = expression | expression @ var
-- matches with the expression; in the later case,
var is bound to the expression
-- handled by translate_pattern
expression = _ |
-- matches anything
-- anything that is not of 'e' type may currently be
matched only by _ without @ or / parts
{ alternatives } |
-- matches if any of alternatives does
[ string ] |
-- expression given by string is compared with the
matched expression using rtx_equal_p; in a special
cases [0], [1], ..., [9], temporary variables
_match[0-9] are used. ['s and ]'s in the string
must be balanced.
-- handled by translate_const_pattern
$var |
-- matches anything, var is bound to the expression
-- handled by translate_var_pattern
( rtx_match ) |
-- matches rtx expression
code
-- a shortcut for ( code )
alternatives = pattern | pattern '|' alternatives
-- handled by translate_alt_pattern
var = [0-9] | string
-- variables 0 - 9 are temporaries, they may be accessed
as _match[0-9]
-- string must be a valid c identifier and it is
interpreted as a variable name
rtx_match = code_match parameters
-- matches code of expression with code_match and
arguments with parameters; if there are less
parameters than appropriate for a code, the other
ones may have any values
-- handled by translate_expr_pattern
parameters = pattern | pattern parameters
code_match = [var] |
-- code must be equal to value of var. Temporary
variables are of course disjoint with rtx temporary
variables and are named _code[0-9]
codeset |
codeset @ var
-- code must belong to a codeset, in the later case
it is assigned to var
codeset = * | !code | code | { codes } | ! { codes } | {} | ! {}
codes = code | code codes
-- a code or list of codes in set, by their names
(in lowercase)
-- with !, complement of the set is taken
-- * means any code
-- handled by translate_code_pattern
Matching is done left to right and without backtracking, i.e. the following
pattern:
{(plus _ $0) | (plus $0 _)} / (_match0 == const0_rtx) /
will match (plus const1_rtx const0_rtx), but not
(plus const0_rtx const1_rtx).
The technical realization is a bit tricky; we emit the macro
MATCH##line number for every occurence of MATCH_RTX that contains the
condition performing the pattern match. MATCH_RTX (P, X) itself expands to
MATCH ## __LINE__ (X) and further to the required condition.
*/
/* Characters that define a temporary variable. */
#define ANON_CHAR(X) ('0' <= (X) && (X) <= '9')
/* Valid characters in the variable names. */
#define IDENT_CHAR(X) (('a' <= (X) && (X) <= 'z') \
|| ('A' <= (X) && (X) <= 'Z') \
|| ('0' <= (X) && (X) <= '9') \
|| (X) == '_')
/* Names of the temporary rtx and rtx_code variables. */
const char *matchvars[10] =
{
"_match0", "_match1", "_match2", "_match3", "_match4",
"_match5", "_match6", "_match7", "_match8", "_match9"
};
const char *codevars[10] =
{
"_code0", "_code1", "_code2", "_code3", "_code4",
"_code5", "_code6", "_code7", "_code8", "_code9"
};
/* Expands names of temporary variables. */
#define MATCH_VAR_NAME(X) ANON_CHAR (*(X)) ? matchvars[*(X) - '0'] : (X)
#define CODE_VAR_NAME(X) ANON_CHAR (*(X)) ? codevars[*(X) - '0'] : (X)
/* The data structure to represent the pattern. */
struct pattern
{
enum pattern_type
{
PATTERN_ANY, /* $variable or _. */
PATTERN_CONST, /* [constant] */
PATTERN_EXPR, /* (code a1 a2 ...) */
PATTERN_OR /* {pat1 | pat2 | ...} */
} type;
char *variable; /* The variable to that the match is stored. */
char *predicate; /* Predicate the match must satisfy. */
union
{
struct data_const
{
char *cnst; /* Constant with that the match is compared. */
} data_const;
struct data_expr
{
int negate_code_alts; /* Whether to take complement of set of codes. */
char *code_variable; /* The variable to store the code in. */
bitmap code_alts; /* The set of possible codes. For [const] this
is NULL. */
int n_params; /* Number of arguments of rtx to match. */
struct pattern **params; /* The patterns for them. */
} data_expr;
struct data_or
{
int n_alts; /* Number of alternatives. */
struct pattern **alts; /* The alternatives. */
} data_or;
} data;
};
int main (int, char **);
static void addChar (char **, int *, int *, char);
static char readchar (FILE *, char **, char **, int *);
static char skipws (FILE *, char **, char **, int *);
static char *read_pattern (FILE *, char **, char **, int *);
static void print_pattern_condition (FILE *, struct pattern *, int,
const char *);
static void print_code_condition (FILE *, int, char *, bitmap, int,
const char *);
static struct pattern *translate_whole_pattern (char *);
static struct pattern *translate_pattern (char **);
static struct pattern *translate_expr_pattern (char **);
static struct pattern *translate_var_pattern (char **);
static struct pattern *translate_alt_pattern (char **);
static struct pattern *translate_const_pattern (char **);
static void translate_alias (char **, struct pattern *);
static void translate_predicate (char **, struct pattern *);
static void translate_code_pattern (char **, int *, bitmap *, char **);
static struct pattern *create_pattern_any (char *);
static struct pattern *create_pattern_const (char *);
static struct pattern *create_pattern_expr (int, bitmap, char *);
static struct pattern *create_pattern_alt (void);
static void add_param (struct pattern *, struct pattern *);
static void add_alt (struct pattern *, struct pattern *);
static void add_code (char *, bitmap, char *);
static void skip_whites (char **);
static char *cut_var_name (char **);
static void doindent (FILE *, int);
static void release_pattern (struct pattern *);
static void generate_match_macros (FILE *, FILE *);
static void report_error (const char *, char *);
/* Variables that define current position in the processed file (for error
messages. */
static char *file_name;
static char *macro_file;
static int line_number;
static char *line_start;
/* Reports an error described by MSG occuring at point WHERE on the current
line. */
static void
report_error (const char *msg, char *where)
{
fprintf (stderr, "Error (%s) at %s:%d char %d (%s of expression %s)",
msg, file_name, line_number, where - line_start,
where, line_start);
unlink (macro_file);
abort ();
}
/* Generates a condition to match PATTERN with expression POSITION.
The condition is written to FILE indented by INDENT spaces. */
static void
print_pattern_condition (FILE *file, struct pattern *pattern, int indent,
const char *position)
{
int i;
if (pattern->variable)
{
fprintf (file, "((%s = %s) || 1) && ",
MATCH_VAR_NAME (pattern->variable), position);
position = MATCH_VAR_NAME (pattern->variable);
}
switch (pattern->type)
{
case PATTERN_ANY:
fprintf (file, "1");
break;
case PATTERN_CONST:
fprintf (file, "rtx_equal_p (%s, %s)",
MATCH_VAR_NAME (pattern->data.data_const.cnst), position);
break;
case PATTERN_OR:
fprintf (file, "(0");
for (i = 0; i < pattern->data.data_or.n_alts; i++)
{
fprintf (file, " \\\n");
doindent (file, indent + 5);
fprintf (file, "|| (");
print_pattern_condition (file,
pattern->data.data_or.alts[i],
indent + 8,
position);
fprintf (file, ")");
}
fprintf (file, " \\\n");
doindent (file, indent);
fprintf (file, ")");
break;
case PATTERN_EXPR:
print_code_condition (file,
pattern->data.data_expr.negate_code_alts,
pattern->data.data_expr.code_variable,
pattern->data.data_expr.code_alts,
indent, position);
for (i = 0; i < pattern->data.data_expr.n_params; i++)
{
char *new_position;
fprintf (file, " \\\n");
doindent (file, indent + 5);
fprintf (file, "&& ");
new_position = xmalloc (strlen (position) + 100);
sprintf (new_position, "XEXP (%s, %d)", position, i);
print_pattern_condition (file,
pattern->data.data_expr.params[i],
indent + 8,
new_position);
free (new_position);
}
break;
}
if (pattern->predicate)
{
fprintf (file, " \\\n");
doindent (file, indent);
if (strchr (pattern->predicate, '('))
{
fprintf (file, "&& ((_loc = %s) || 1) \\\n", position);
doindent (file, indent);
fprintf (file, "&& (%s)", pattern->predicate);
}
else
fprintf (file, "&& %s (%s)", pattern->predicate, position);
}
}
/* Generates a condition to match set of codes ALTS (its complement if NEGATED)
against rtx code of expression POSITION. The result is stored into VARIABLE
if it is not NULL. The condition is written to FILE indented by INDENT
spaces. */
static void
print_code_condition (FILE *file, int negated, char *variable, bitmap alts,
int indent, const char *position)
{
int code;
if (!alts)
{
fprintf (file, "%s == GET_CODE (%s)",
CODE_VAR_NAME (variable), position);
return;
}
if (variable)
fprintf (file, "((%s = GET_CODE (%s)) || 1) &&",
CODE_VAR_NAME (variable), position);
fprintf (file, negated ? "(1" : "(0");
EXECUTE_IF_SET_IN_BITMAP (alts, 0, code,
{
fprintf (file, " \\\n");
doindent (file, indent + 5);
fprintf (file, negated ? "&& " : "|| ");
fprintf (file, "GET_CODE (%s) %s %d /* %s */",
position, negated ? "!=" : "==",
code, GET_RTX_NAME (code));
});
fprintf (file, " \\\n");
doindent (file, indent);
fprintf (file, ")");
}
/* Outputs INDENT spaces to FILE. */
static void
doindent (FILE *file, int indent)
{
while (indent--)
fprintf (file, " ");
}
/* Parses the PATTERN and returns its internal representation. */
static struct pattern *
translate_whole_pattern (char *pattern)
{
struct pattern *ret = translate_pattern (&pattern);
skip_whites (&pattern);
if (*pattern)
report_error ("extra characters after end of pattern", pattern);
return ret;
}
/* Moves pointer to PATTERN over any spaces. */
static void
skip_whites (char **pattern)
{
while (**pattern == ' ')
(*pattern)++;
}
/* Translates pattern case of the grammar, moving the pointer to PATTERN behind
it. */
static struct pattern *
translate_pattern (char **pattern)
{
char *pos;
struct pattern *ret;
skip_whites (pattern);
pos = *pattern;
*pattern = pos + 1;
switch (*pos)
{
case '(':
ret = translate_expr_pattern (pattern);
break;
case '{':
ret = translate_alt_pattern (pattern);
break;
case '[':
ret = translate_const_pattern (pattern);
break;
case '_':
ret = create_pattern_any (NULL);
break;
case '$':
ret = translate_var_pattern (pattern);
break;
default:
*pattern = pos;
/* A special case for code being shortcut for (code) */
if (IDENT_CHAR (*pos))
{
int negate_alts; /* Of course always zero. */
bitmap code_alts;
char *variable; /* It will get detected as code variable if present.
Does not matter, we interpret it as match variable
instead. */
translate_code_pattern (pattern, &negate_alts, &code_alts, &variable);
ret = create_pattern_expr (negate_alts, code_alts, NULL);
ret->variable = variable;
break;
}
return NULL;
}
skip_whites (pattern);
if (**pattern == '@')
{
(*pattern)++;
translate_alias (pattern, ret);
}
skip_whites (pattern);
if (**pattern == '/')
{
(*pattern)++;
translate_predicate (pattern, ret);
}
return ret;
}
/* Translates expression case of the grammar, moving the pointer to PATTERN behind
it. */
static struct pattern *
translate_expr_pattern (char **pattern)
{
int negate_alts;
bitmap code_alts;
char *code_variable;
struct pattern *ret, *param;
translate_code_pattern (pattern, &negate_alts, &code_alts, &code_variable);
ret = create_pattern_expr (negate_alts, code_alts, code_variable);
while ((param = translate_pattern (pattern)))
add_param (ret, param);
if (**pattern != ')')
report_error ("expecting end of pattern", *pattern);
(*pattern)++;
return ret;
}
/* Adds a pattern matching single parameter PARAM to the list of parameters
of rtl_match pattern TO. */
static void
add_param (struct pattern *to, struct pattern *param)
{
struct data_expr *tto = &to->data.data_expr;
tto->params = xrealloc (tto->params, sizeof (struct pattern *) * ++tto->n_params);
tto->params[tto->n_params - 1] = param;
}
/* Translates alt case of the grammar, moving the pointer to PATTERN behind
it. */
static struct pattern *
translate_alt_pattern (char **pattern)
{
struct pattern *ret, *alt;
ret = create_pattern_alt ();
while (1)
{
alt = translate_pattern (pattern);
add_alt (ret, alt);
skip_whites (pattern);
if (**pattern == '}')
break;
if (**pattern != '|')
report_error ("expecting alternative", *pattern);
(*pattern)++;
}
(*pattern)++;
return ret;
}
/* Adds a pattern ALT to the list of alternatives of pattern TO. */
static void
add_alt (struct pattern *to, struct pattern *alt)
{
struct data_or *tto = &to->data.data_or;
tto->alts = xrealloc (tto->alts, sizeof (struct pattern *) * ++tto->n_alts);
tto->alts[tto->n_alts - 1] = alt;
}
/* Translates const case of the grammar, moving the pointer to PATTERN behind
it. */
static struct pattern *
translate_const_pattern (char **pattern)
{
int level = 1, l;
char *beg = *pattern;
char *end = beg;
char *ret;
while (level)
{
if (*end == '[')
level++;
if (*end == ']')
level--;
if (!*end)
report_error ("unclosed const pattern", beg);
end++;
}
l = end - beg;
ret = xmalloc (l);
memcpy (ret, beg, l - 1);
ret[l - 1] = 0;
*pattern = end;
return create_pattern_const (ret);
}
/* Cuts a variable name from pattern and moves pointer to PATTERN behind it.
The variable name is returned. */
static char *
cut_var_name (char **pattern)
{
int l;
char *beg = *pattern;
char *end = beg;
char *ret;
if (ANON_CHAR (*beg))
end = beg + 1;
else if (IDENT_CHAR (*beg))
{
while (IDENT_CHAR (*end))
end++;
}
else report_error ("wrong variable name", *pattern);
l = end - beg;
ret = xmalloc (l + 1);
memcpy (ret, beg, l);
ret[l] = 0;
*pattern = end;
return ret;
}
/* Translates var case of the grammar, moving the pointer to PATTERN behind
it. */
static struct pattern *
translate_var_pattern (char **pattern)
{
return create_pattern_any (cut_var_name (pattern));
}
/* Translates @ var part of pattern case of the grammar, moving the pointer
to PATTERN behind it and storing var name in TGT. */
static void
translate_alias (char **pattern, struct pattern *tgt)
{
char *name = cut_var_name (pattern);
tgt->variable = name;
}
/* Translates / predicate / part of pattern case of the grammar, moving
the pointer to PATTERN behind it and storing the predicate in TGT. */
static void
translate_predicate (char **pattern, struct pattern *tgt)
{
int l;
char *beg = *pattern;
char *end = beg;
char *predicate;
while (*end != '/')
{
if (!*end)
report_error ("unclosed predicate", beg);
end++;
}
l = end - beg + 1;
predicate = xmalloc (l);
memcpy (predicate, beg, l - 1);
predicate[l - 1] = 0;
tgt->predicate = predicate;
*pattern = end + 1;
}
/* Adds a CODE parsed from PATTERN at the actual position to codeset TO. */
static void
add_code (char *pattern, bitmap to, char *code)
{
int i;
for (i = 0; i < NUM_RTX_CODE; i++)
if (! strcmp (code, GET_RTX_NAME (i)))
break;
if (i == NUM_RTX_CODE)
report_error ("wrong rtl code", pattern);
bitmap_set_bit (to, i);
}
/* Translates code_match case of the grammar, moving the pointer
to PATTERN behind it. It returns set of alternatives for code in CODE_ALTS,
variable to that store it in VARIABLE (or NULL if none) and whether to
consider a complement of the set in NEGATE_ALTS.
If the code match is a comparison with code variable, NULL is returned in
CODE_ALTS and name of the variable in VARIABLE. */
static void
translate_code_pattern (char **pattern, int *negate_alts, bitmap *code_alts,
char **variable)
{
bitmap ret;
char *code, *tmp;
skip_whites (pattern);
if (**pattern == '[')
{
(*pattern)++;
skip_whites (pattern);
*variable = cut_var_name (pattern);
skip_whites (pattern);
if (**pattern != ']')
report_error ("unclosed variable reference", *pattern);
(*pattern)++;
*code_alts = NULL;
return;
}
if (**pattern == '!')
{
(*pattern)++;
skip_whites (pattern);
*negate_alts = true;
}
else
*negate_alts = false;
ret = BITMAP_XMALLOC ();
if (**pattern == '{')
{
(*pattern)++;
skip_whites (pattern);
if (**pattern != '}')
{
while (1)
{
tmp = *pattern;
code = cut_var_name (pattern);
add_code (tmp, ret, code);
free (code);
skip_whites (pattern);
if (**pattern == '}')
break;
if (**pattern != '|')
report_error ("expecting alternative", *pattern);
(*pattern)++;
skip_whites (pattern);
}
}
(*pattern)++;
}
else if (**pattern == '*')
{
*negate_alts = !*negate_alts;
(*pattern)++;
}
else
{
tmp = *pattern;
skip_whites (pattern);
code = cut_var_name (pattern);
add_code (tmp, ret, code);
free (code);
}
*code_alts = ret;
skip_whites (pattern);
if (**pattern == '@')
{
(*pattern)++;
skip_whites (pattern);
*variable = cut_var_name (pattern);
}
else
*variable = NULL;
}
/* Create pattern expression matching anything and storing it to VAR (if VAR is
not NULL. */
static struct pattern *
create_pattern_any (char *var)
{
struct pattern *ret = xmalloc (sizeof (struct pattern));
ret->type = PATTERN_ANY;
ret->variable = var;
ret->predicate = NULL;
return ret;
}
/* Create pattern expression matching anything rtx_equal_p to VALUE. */
static struct pattern *
create_pattern_const (char *value)
{
struct pattern *ret = xmalloc (sizeof (struct pattern));
ret->type = PATTERN_CONST;
ret->variable = NULL;
ret->predicate = NULL;
ret->data.data_const.cnst = value;
return ret;
}
/* Create pattern expression matching rtx with code matched by CODE_ALTS,
storing this code to CODE_VAR if not NULL. If NEGATE_ALTS is set,
a pattern for complement of the CODE_ALTS is taken. */
static struct pattern *
create_pattern_expr (int negate_alts, bitmap code_alts, char *code_var)
{
struct pattern *ret = xmalloc (sizeof (struct pattern));
ret->type = PATTERN_EXPR;
ret->variable = NULL;
ret->predicate = NULL;
ret->data.data_expr.negate_code_alts = negate_alts;
ret->data.data_expr.code_variable = code_var;
ret->data.data_expr.code_alts = code_alts;
ret->data.data_expr.n_params = 0;
ret->data.data_expr.params = NULL;
return ret;
}
/* Create pattern expression matching alternatives (that will be added
later). */
static struct pattern *
create_pattern_alt ()
{
struct pattern *ret = xmalloc (sizeof (struct pattern));
ret->type = PATTERN_OR;
ret->variable = NULL;
ret->predicate = NULL;
ret->data.data_or.n_alts = 0;
ret->data.data_or.alts = NULL;
return ret;
}
/* Releases a memory occupied by PATTERN. */
static void
release_pattern (struct pattern *pattern)
{
int i;
if (pattern->variable)
free (pattern->variable);
if (pattern->predicate)
free (pattern->predicate);
switch (pattern->type)
{
case PATTERN_ANY:
break;
case PATTERN_CONST:
free (pattern->data.data_const.cnst);
break;
case PATTERN_EXPR:
if (pattern->data.data_expr.code_variable)
free (pattern->data.data_expr.code_variable);
if (pattern->data.data_expr.code_alts)
BITMAP_FREE (pattern->data.data_expr.code_alts);
for (i = 0; i < pattern->data.data_expr.n_params; i++)
release_pattern (pattern->data.data_expr.params[i]);
free (pattern->data.data_expr.params);
break;
case PATTERN_OR:
for (i = 0; i < pattern->data.data_or.n_alts; i++)
release_pattern (pattern->data.data_or.alts[i]);
free (pattern->data.data_or.alts);
break;
}
free (pattern);
}
/* Read char from file SOURCE starting at *START character and return it.
Keep track of the line number in LINE. *LINESTART is the beginning of
the current line (*START is pointer into it). */
static char
readchar (FILE *source, char **linestart, char **start, int *line)
{
unsigned ll;
if (!*start)
return 0;
if (**start)
{
char ret = *(*start)++;
if (ret == '\r' || ret == '\t' || ret == '\n')
ret = ' ';
return ret;
}
free (*linestart);
*linestart = NULL;
*start = NULL;
if (getline (linestart, &ll, source) < 0)
return 0;
(*line)++;
*start = *linestart;
return ' ';
}
/* Read chars from file SOURCE starting at *START till the first non-whitespace
character and return it. Keep track of the line number in LINE. *LINESTART
is the beginning of the current line (*START is pointer into it). */
static char
skipws (FILE *source, char **linestart, char **start, int *line)
{
char ret;
while ((ret = readchar (source, linestart, start, line)) == ' ')
continue;
return ret;
}
/* Adds char CH to end of *STRING of length *LEN (the lenght of array
allocated for string is *MAXLEN). */
static void
addChar (char **string, int *len, int *maxlen, char ch)
{
if (*len == *maxlen)
{
*maxlen = 2 * *maxlen + 1;
*string = xrealloc (*string, *maxlen + 1);
}
(*string)[(*len)++] = ch;
(*string)[*len] = 0;
}
/* Reads arguments of MATCH_RTX starting at *START, reading following lines
from file SOURCE if necessary; records the last line number read in *LINE
and set *START to beginning of the unread part of file. Returns
the first argument of MATCH_RTX or NULL if there is some error. *LINESTART
is the beginning of the current line (*START is pointer into it). */
static char *
read_pattern (FILE *source, char **linestart, char **start, int *line)
{
char *arg = xmalloc (101);
int arglen = 0, argmax = 100;
char act;
int escape = 0, level = 0;
arg[0] = 0;
act = skipws (source, linestart, start, line);
if (act != '(')
return NULL;
act = skipws (source, linestart, start, line);
if (act != '"')
return NULL;
while (1)
{
act = readchar (source, linestart, start, line);
if (!act)
return NULL;
if (escape)
{
escape = 0;
addChar (&arg, &arglen, &argmax, act);
continue;
}
if (act == '\\')
{
escape = 1;
continue;
}
if (act == '"')
break;
addChar (&arg, &arglen, &argmax, act);
}
act = skipws (source, linestart, start, line);
if (act != ',')
return NULL;
while (1)
{
act = readchar (source, linestart, start, line);
if (!act)
return NULL;
if (act == '(')
level++;
else if (act == ')')
level--;
if (level < 0)
break;
}
return arg;
}
/* Generate MATCH... macros for file SOURCE, storing them at MACROFILE. */
static void
generate_match_macros (FILE *source, FILE *macrofile)
{
char *line = NULL, *match, *tmp, *patline;
int nl = 0;
size_t ll;
struct pattern *pattern;
for (; getline (&line, &ll, source) >= 0; free (line), line = NULL)
{
nl++;
match = strstr (line, "MATCH_RTX");
if (!match)
continue;
tmp = match = xstrdup (match + 9);
patline = read_pattern (source, &match, &tmp, &nl);
if (match)
free (match);
if (!patline)
{
fprintf (stderr, "Malformed pattern match at %s:%d.\n",
file_name, nl);
abort ();
}
line_start = patline;
line_number = nl;
pattern = translate_whole_pattern (patline);
fprintf (macrofile, "#define MATCH%d(X) (_last_match = \\\n ", nl);
print_pattern_condition (macrofile, pattern, 4, "(X)");
fprintf (macrofile, ")\n\n");
release_pattern (pattern);
free (patline);
}
}
/* Processes the file ARGV[1], storing the result in file whose name is
derived from it by replacing .c suffix with .h and prefixing the name
by match- prefix. */
int
main (int argc, char **argv)
{
FILE *source, *macro;
int i, len;
if (argc != 2)
abort ();
file_name = argv[1];
source = fopen (argv[1], "r");
len = strlen (file_name);
macro_file = xmalloc (len + 7);
macro_file[len + 6] = 0;
if (len < 2 || file_name[len - 1] != 'c' || file_name[len - 2] != '.')
abort ();
macro_file[len + 5] = 'h';
for (i = len - 2; i >= 0; i--)
{
if (file_name[i] == '/')
break;
macro_file[i + 6] = file_name[i];
}
macro_file[i + 1] = 'm';
macro_file[i + 2] = 'a';
macro_file[i + 3] = 't';
macro_file[i + 4] = 'c';
macro_file[i + 5] = 'h';
macro_file[i + 6] = '-';
for (; i >= 0; i--)
macro_file[i] = file_name[i];
macro = fopen (macro_file, "w");
if (!source || !macro)
abort ();
fprintf (macro, "/* Generated automatically by the program `genpattern'\n");
fprintf (macro, " from file `%s' */\n\n", file_name);
fprintf (macro, "#define USES_PATTERN_MATCHING \\\n");
for (i = 0; i < 10; i++)
{
fprintf (macro, " rtx %s ATTRIBUTE_UNUSED; \\\n", matchvars[i]);
fprintf (macro, " enum rtx_code %s ATTRIBUTE_UNUSED; \\\n", codevars[i]);
}
fprintf (macro, "\\\n");
fprintf (macro, " rtx _loc ATTRIBUTE_UNUSED; \\\n");
fprintf (macro, " /* We let this produce warning in case pattern matching is never used. */ \\\n");
fprintf (macro, " int _last_match\n\n");
fprintf (macro, "#define CONCATX(A, B) A ## B\n");
fprintf (macro, "#define CONCAT(A, B) CONCATX (A, B) /* A trick to get __LINE__ expanded. */\n\n");
fprintf (macro, "#define MATCH_RTX(P, X) CONCAT (MATCH, __LINE__) (X)\n\n");
generate_match_macros (source, macro);
fclose (source);
fclose (macro);
return 0;
}
doc/match.texi:
===============
@c Copyright (C) 2003
@c Free Software Foundation, Inc.
@c This is part of the GCC manual.
@c For copying conditions, see the file gcc.texi.
@node RTL Pattern Matching
@chapter RTL Pattern Matching
@cindex pattern matching
@findex MATCH_RTX
We often need to do a pattern matching on RTL expressions. It is quite
complicated to do it in C and the resulting conditions are very hard to
read. To simplify the task, the following solution was devised:
Instead of writing condition like
@smallexample
if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
@{
base = XEXP (XEXP (addr, 0), 0);
offset = INTVAL (XEXP (XEXP (addr, 0), 1));
@}
@end smallexample
we write
@smallexample
if (MATCH_RTX ("(const (plus $base const_int@@1))", addr))
offset = INTVAL (_match1);
@end smallexample
The @code{MATCH_RTX} construction is translated into the code equivalent to the
one above in the compile time by using a macro machinery and generator
@file{genpattern}, so there is no runtime penalty for using it.
@menu
* Pattern Grammar:: What patterns you may use with MATCH_RTX.
* Source Files:: How to let @file{genpattern} know about your files.
@end menu
@node Pattern Grammar
@section Pattern Grammar
Exact (almost; for easier reading some conditions on it are only mentioned
in comments) grammar of pattern expressions is
@verbatim
pattern = vexpression | vexpression / predicate /
-- if predicate is supplied (any text that does not
contain a /), value matched by vexpression is bound
to _loc variable and predicate is requiered to be
true. If predicate does not contain ( character,
the location is passed to it as argument instead
pexpression = expression | expression @ var
-- matches with the expression; in the later case,
var is bound to the expression
expression = _ |
-- matches anything
-- anything that is not of 'e' type may currently be
matched only by _ without @ or / parts
{ alternatives } |
-- matches if any of alternatives does
[ string ] |
-- expression given by string is compared with the
matched expression using rtx_equal_p; in a special
cases [0], [1], ..., [9], temporary variables
_match[0-9] are used. ['s and ]'s in the string
must be balanced.
$var |
-- matches anything, var is bound to the expression
( rtx_match ) |
-- matches rtx expression
code
-- a shortcut for ( code )
alternatives = pattern | pattern '|' alternatives
var = [0-9] | string
-- variables 0 - 9 are temporaries, they may be accessed
as _match[0-9]
-- string must be a valid c identifier and it is
interpreted as a variable name
rtx_match = code_match parameters
-- matches code of expression with code_match and
arguments with parameters; if there are less
parameters than appropriate for a code, the other
ones may have any values
parameters = pattern | pattern parameters
code_match = [var] |
-- code must be equal to value of var. Temporary
variables are of course disjoint with rtx temporary
variables and are named _code[0-9]
codeset |
codeset @ var
-- code must belong to a codeset, in the later case
it is assigned to var
codeset = * | !code | code | { codes } | ! { codes } | {} | ! {}
codes = code | code codes
-- a code or list of codes in set, by their names
(in lowercase)
-- with !, complement of the set is taken
-- * means any code
@end verbatim
Matching is done left to right and without backtracking, i.e. the following
pattern:
@smallexample
@{(plus _ $0) | (plus $0 _)@} / (_match0 == const0_rtx) /
@end smallexample
will match @code{(plus const1_rtx const0_rtx)}, but not
@code{(plus const0_rtx const1_rtx)}.
@node Source Files
@section Source Files With MATCH_RTX
@cindex generated files
@cindex files, generated
There is some additional stuff you must do when you want to use this
feature:
@enumerate
@item
You must include a header file generated by @file{genpattern}. Its name
is derived from the name of the source file by replacing @file{.c} suffix
by @file{.h} and adding @file{match-} prefix in front of it. For example
for @file{cse.c} the generated file is called @file{match-cse.h}. Do not
forget to add dependency on this header to @file{Makefile.in}.
@item
To variable declarations for any function that uses pattern matching you
must add a statement @code{USES_PATTERN_MATCHING;} -- it expands to declaration
of @code{_match} and @code{_code} variables the pattern matching uses.
@item
In @file{Makefile.in} you must add the name of source file to variable
@code{MATCHFILES} and the generated header's name to dependency list of
@file{s-match}.
@end enumerate
Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1085
diff -c -3 -p -r1.1085 Makefile.in
*** Makefile.in 18 Jun 2003 19:43:44 -0000 1.1085
--- Makefile.in 18 Jun 2003 21:26:31 -0000
*************** STAGESTUFF = *$(objext) insn-flags.h ins
*** 832,838 ****
insn-output.c insn-recog.c insn-emit.c insn-extract.c insn-peep.c \
insn-attr.h insn-attrtab.c insn-opinit.c insn-constants.h tm-preds.h \
tree-check.h insn-conditions.c \
! s-flags s-config s-codes s-mlib s-genrtl s-gtype gtyp-gen.h \
s-output s-recog s-emit s-extract s-peep s-check s-conditions \
s-attr s-attrtab s-opinit s-preds s-constants s-crt0 \
genemit$(build_exeext) genoutput$(build_exeext) genrecog$(build_exeext) \
--- 832,838 ----
insn-output.c insn-recog.c insn-emit.c insn-extract.c insn-peep.c \
insn-attr.h insn-attrtab.c insn-opinit.c insn-constants.h tm-preds.h \
tree-check.h insn-conditions.c \
! s-flags s-config s-codes s-mlib s-genrtl s-gtype gtyp-gen.h s-match \
s-output s-recog s-emit s-extract s-peep s-check s-conditions \
s-attr s-attrtab s-opinit s-preds s-constants s-crt0 \
genemit$(build_exeext) genoutput$(build_exeext) genrecog$(build_exeext) \
*************** STAGESTUFF = *$(objext) insn-flags.h ins
*** 844,850 ****
genrtl.c genrtl.h gt-*.h gtype-*.h gtype-desc.c \
xgcc$(exeext) cpp$(exeext) cc1$(exeext) $(EXTRA_PASSES) \
$(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
! protoize$(exeext) unprotoize$(exeext) \
$(SPECS) collect2$(exeext) $(USE_COLLECT2) \
gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
*.[0-9][0-9].* *.[si] libcpp.a libbackend.a libgcc.mk \
--- 844,850 ----
genrtl.c genrtl.h gt-*.h gtype-*.h gtype-desc.c \
xgcc$(exeext) cpp$(exeext) cc1$(exeext) $(EXTRA_PASSES) \
$(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
! protoize$(exeext) unprotoize$(exeext) match-*.h genpattern$(exeext) \
$(SPECS) collect2$(exeext) $(USE_COLLECT2) \
gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
*.[0-9][0-9].* *.[si] libcpp.a libbackend.a libgcc.mk \
*************** s-check : gencheck$(build_exeext) $(srcd
*** 1383,1388 ****
--- 1383,1398 ----
$(SHELL) $(srcdir)/move-if-change tmp-check.h tree-check.h
$(STAMP) s-check
+ genpattern$(build_exeext) : genpattern.o $(BUILD_RTL) $(BUILD_SUPPORT) \
+ $(BUILD_ERRORS) $(BUILD_LIBDEPS)
+ $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o $@ \
+ genpattern.o $(BUILD_RTL) $(BUILD_SUPPORT) $(BUILD_ERRORS) $(BUILD_LIBS)
+
+ genpattern.o: genpattern.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
+ bitmap.h
+ $(CC_FOR_BUILD) -c $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(INCLUDES) \
+ $(srcdir)/genpattern.c $(OUTPUT_OPTION)
+
gencheck$(build_exeext) : gencheck.o $(BUILD_LIBDEPS)
$(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o $@ \
gencheck.o $(BUILD_LIBS)
*************** cselib.o : cselib.c $(CONFIG_H) $(SYSTEM
*** 1608,1614 ****
cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
output.h function.h $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
! except.h $(TARGET_H) $(PARAMS_H)
gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h ggc.h $(RECOG_H) $(EXPR_H) \
$(BASIC_BLOCK_H) function.h output.h toplev.h $(TM_P_H) $(PARAMS_H) except.h gt-gcse.h
--- 1618,1624 ----
cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
output.h function.h $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
! except.h $(TARGET_H) $(PARAMS_H) match-cse.h
gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h ggc.h $(RECOG_H) $(EXPR_H) \
$(BASIC_BLOCK_H) function.h output.h toplev.h $(TM_P_H) $(PARAMS_H) except.h gt-gcse.h
*************** s-gtype: gengtype$(build_exeext) $(GTFIL
*** 2051,2056 ****
--- 2061,2075 ----
$(RUN_GEN) ./gengtype
$(STAMP) s-gtype
+ MATCHFILES=$(srcdir)/cse.c
+ match-cse.h: s-match ; @true
+
+ s-match: genpattern$(build_exeext) $(MATCHFILES)
+ for file in $(MATCHFILES) ; do \
+ $(RUN_GEN) ./genpattern$(build_exeext) $$file ; \
+ done
+ $(STAMP) s-match
+
#
# Compile the programs that generate insn-* from the machine description.
# They are compiled with $(CC_FOR_BUILD), and associated libraries,
*************** TEXI_GCCINT_FILES = $(docdir)/gccint.tex
*** 2664,2670 ****
$(docdir)/gnu.texi $(docdir)/include/gpl.texi \
$(docdir)/include/fdl.texi $(docdir)/contrib.texi \
$(docdir)/languages.texi $(docdir)/sourcebuild.texi \
! $(docdir)/gty.texi $(docdir)/libgcc.texi
TEXI_GCCINSTALL_FILES = $(docdir)/install.texi $(docdir)/install-old.texi \
$(docdir)/include/fdl.texi
--- 2683,2689 ----
$(docdir)/gnu.texi $(docdir)/include/gpl.texi \
$(docdir)/include/fdl.texi $(docdir)/contrib.texi \
$(docdir)/languages.texi $(docdir)/sourcebuild.texi \
! $(docdir)/gty.texi $(docdir)/libgcc.texi $(docdir)/match.texi
TEXI_GCCINSTALL_FILES = $(docdir)/install.texi $(docdir)/install-old.texi \
$(docdir)/include/fdl.texi
Index: cse.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cse.c,v
retrieving revision 1.264
diff -c -3 -p -r1.264 cse.c
*** cse.c 4 Jun 2003 21:05:20 -0000 1.264
--- cse.c 18 Jun 2003 21:26:31 -0000
*************** Software Foundation, 59 Temple Place - S
*** 43,48 ****
--- 43,49 ----
#include "except.h"
#include "target.h"
#include "params.h"
+ #include "match-cse.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
*************** static bool
*** 667,672 ****
--- 668,675 ----
fixed_base_plus_p (x)
rtx x;
{
+ USES_PATTERN_MATCHING;
+
switch (GET_CODE (x))
{
case REG:
*************** fixed_base_plus_p (x)
*** 680,688 ****
return false;
case PLUS:
! if (GET_CODE (XEXP (x, 1)) != CONST_INT)
! return false;
! return fixed_base_plus_p (XEXP (x, 0));
case ADDRESSOF:
return true;
--- 683,689 ----
return false;
case PLUS:
! return MATCH_RTX ("(plus _/fixed_base_plus_p/ const_int)", x);
case ADDRESSOF:
return true;
*************** address_cost (x, mode)
*** 897,908 ****
rtx x;
enum machine_mode mode;
{
/* The address_cost target hook does not deal with ADDRESSOF nodes. But,
during CSE, such nodes are present. Using an ADDRESSOF node which
refers to the address of a REG is a good thing because we can then
turn (MEM (ADDRESSSOF (REG))) into just plain REG. */
! if (GET_CODE (x) == ADDRESSOF && REG_P (XEXP ((x), 0)))
return -1;
/* We may be asked for cost of various unusual addresses, such as operands
--- 898,910 ----
rtx x;
enum machine_mode mode;
{
+ USES_PATTERN_MATCHING;
/* The address_cost target hook does not deal with ADDRESSOF nodes. But,
during CSE, such nodes are present. Using an ADDRESSOF node which
refers to the address of a REG is a good thing because we can then
turn (MEM (ADDRESSSOF (REG))) into just plain REG. */
! if (MATCH_RTX ("(addressof _/REG_P/)", x))
return -1;
/* We may be asked for cost of various unusual addresses, such as operands
*************** static int
*** 1164,1169 ****
--- 1166,1172 ----
mention_regs (x)
rtx x;
{
+ USES_PATTERN_MATCHING;
enum rtx_code code;
int i, j;
const char *fmt;
*************** mention_regs (x)
*** 1232,1250 ****
if (code == COMPARE || GET_RTX_CLASS (code) == '<')
{
! if (GET_CODE (XEXP (x, 0)) == REG
! && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
! if (insert_regs (XEXP (x, 0), NULL, 0))
{
! rehash_using_reg (XEXP (x, 0));
changed = 1;
}
! if (GET_CODE (XEXP (x, 1)) == REG
! && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
! if (insert_regs (XEXP (x, 1), NULL, 0))
{
! rehash_using_reg (XEXP (x, 1));
changed = 1;
}
}
--- 1235,1251 ----
if (code == COMPARE || GET_RTX_CLASS (code) == '<')
{
! if (MATCH_RTX ("(* reg@0/!REGNO_QTY_VALID_P (REGNO (_loc))/)", x))
! if (insert_regs (_match0, NULL, 0))
{
! rehash_using_reg (_match0);
changed = 1;
}
! if (MATCH_RTX ("(* _ reg@1/!REGNO_QTY_VALID_P (REGNO (_loc))/)", x))
! if (insert_regs (_match1, NULL, 0))
{
! rehash_using_reg (_match1);
changed = 1;
}
}
*************** canon_hash (x, mode)
*** 2237,2242 ****
--- 2238,2244 ----
rtx x;
enum machine_mode mode;
{
+ USES_PATTERN_MATCHING;
int i, j;
unsigned hash = 0;
enum rtx_code code;
*************** canon_hash (x, mode)
*** 2378,2388 ****
handling since the MEM may be BLKmode which normally
prevents an entry from being made. Pure calls are
marked by a USE which mentions BLKmode memory. */
! if (GET_CODE (XEXP (x, 0)) == MEM
! && ! MEM_VOLATILE_P (XEXP (x, 0)))
{
hash += (unsigned) USE;
! x = XEXP (x, 0);
if (! RTX_UNCHANGING_P (x) || fixed_base_plus_p (XEXP (x, 0)))
hash_arg_in_memory = 1;
--- 2380,2389 ----
handling since the MEM may be BLKmode which normally
prevents an entry from being made. Pure calls are
marked by a USE which mentions BLKmode memory. */
! if (MATCH_RTX ("(use mem@0/!MEM_VOLATILE_P/)", x))
{
hash += (unsigned) USE;
! x = _match0;
if (! RTX_UNCHANGING_P (x) || fixed_base_plus_p (XEXP (x, 0)))
hash_arg_in_memory = 1;
*************** cse_rtx_varies_p (x, from_alias)
*** 2714,2719 ****
--- 2715,2721 ----
rtx x;
int from_alias;
{
+ USES_PATTERN_MATCHING;
/* We need not check for X and the equivalence class being of the same
mode because if X is equivalent to a constant in some mode, it
doesn't vary in any mode. */
*************** cse_rtx_varies_p (x, from_alias)
*** 2729,2743 ****
return 0;
}
! if (GET_CODE (x) == PLUS
! && GET_CODE (XEXP (x, 1)) == CONST_INT
! && GET_CODE (XEXP (x, 0)) == REG
! && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
{
! int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
! if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX)
return 0;
}
--- 2731,2743 ----
return 0;
}
! if (MATCH_RTX ("(plus reg@0/REGNO_QTY_VALID_P (REGNO (_loc))/ \
! const_int)", x))
{
! int x0_q = REG_QTY (REGNO (_match0));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
! if ((GET_MODE (_match0) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX)
return 0;
}
*************** cse_rtx_varies_p (x, from_alias)
*** 2747,2766 ****
us a three instruction sequence, load large offset into a register,
load fp minus a constant into a register, then a MEM which is the
sum of the two `constant' registers. */
! if (GET_CODE (x) == PLUS
! && GET_CODE (XEXP (x, 0)) == REG
! && GET_CODE (XEXP (x, 1)) == REG
! && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
! && REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
{
! int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
! int x1_q = REG_QTY (REGNO (XEXP (x, 1)));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
struct qty_table_elem *x1_ent = &qty_table[x1_q];
! if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX
! && (GET_MODE (XEXP (x, 1)) == x1_ent->mode)
&& x1_ent->const_rtx != NULL_RTX)
return 0;
}
--- 2747,2763 ----
us a three instruction sequence, load large offset into a register,
load fp minus a constant into a register, then a MEM which is the
sum of the two `constant' registers. */
! if (MATCH_RTX ("(plus reg@0/REGNO_QTY_VALID_P (REGNO (_loc))/ \
! reg@1/REGNO_QTY_VALID_P (REGNO (_loc))/)", x))
{
! int x0_q = REG_QTY (REGNO (_match0));
! int x1_q = REG_QTY (REGNO (_match1));
struct qty_table_elem *x0_ent = &qty_table[x0_q];
struct qty_table_elem *x1_ent = &qty_table[x1_q];
! if ((GET_MODE (_match0) == x0_ent->mode)
&& x0_ent->const_rtx != NULL_RTX
! && (GET_MODE (_match1) == x1_ent->mode)
&& x1_ent->const_rtx != NULL_RTX)
return 0;
}
*************** find_best_addr (insn, loc, mode)
*** 2884,2891 ****
rtx *loc;
enum machine_mode mode;
{
struct table_elt *elt;
! rtx addr = *loc;
struct table_elt *p;
int found_better = 1;
int save_do_not_record = do_not_record;
--- 2881,2889 ----
rtx *loc;
enum machine_mode mode;
{
+ USES_PATTERN_MATCHING;
struct table_elt *elt;
! rtx addr = *loc, op1 = NULL_RTX;
struct table_elt *p;
int found_better = 1;
int save_do_not_record = do_not_record;
*************** find_best_addr (insn, loc, mode)
*** 2903,2919 ****
for some reason, but we cannot take advantage of that because we have
no easy way to unshare the MEM. In addition, looking up all stack
addresses is costly. */
! if ((GET_CODE (addr) == PLUS
! && GET_CODE (XEXP (addr, 0)) == REG
! && GET_CODE (XEXP (addr, 1)) == CONST_INT
! && (regno = REGNO (XEXP (addr, 0)),
! regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
! || regno == ARG_POINTER_REGNUM))
! || (GET_CODE (addr) == REG
! && (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
! || GET_CODE (addr) == ADDRESSOF
|| CONSTANT_ADDRESS_P (addr))
return;
--- 2901,2911 ----
for some reason, but we cannot take advantage of that because we have
no easy way to unshare the MEM. In addition, looking up all stack
addresses is costly. */
! if ((MATCH_RTX ("{(plus reg@0 const_int) | reg@0}", addr)
! && (regno = REGNO (_match0), regno == FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
! || MATCH_RTX ("addressof", addr)
|| CONSTANT_ADDRESS_P (addr))
return;
*************** find_best_addr (insn, loc, mode)
*** 3009,3024 ****
if (flag_expensive_optimizations
&& (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
|| GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
! && GET_CODE (XEXP (*loc, 0)) == REG)
{
- rtx op1 = XEXP (*loc, 1);
-
do_not_record = 0;
! hash = HASH (XEXP (*loc, 0), Pmode);
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
! elt = lookup (XEXP (*loc, 0), hash, Pmode);
if (elt == 0)
return;
--- 3001,3014 ----
if (flag_expensive_optimizations
&& (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
|| GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
! && MATCH_RTX ("(* reg@0 $op1)", *loc))
{
do_not_record = 0;
! hash = HASH (_match0, Pmode);
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
! elt = lookup (_match0, hash, Pmode);
if (elt == 0)
return;
*************** find_comparison_args (code, parg1, parg2
*** 3100,3105 ****
--- 3090,3096 ----
rtx *parg1, *parg2;
enum machine_mode *pmode1, *pmode2;
{
+ USES_PATTERN_MATCHING;
rtx arg1, arg2;
arg1 = *parg1, arg2 = *parg2;
*************** find_comparison_args (code, parg1, parg2
*** 3268,3274 ****
}
else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
! arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
}
/* Return our results. Return the modes from before fold_rtx
--- 3259,3265 ----
}
else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
! MATCH_RTX ("(* $arg1 $arg2)", x);
}
/* Return our results. Return the modes from before fold_rtx
*************** fold_rtx (x, insn)
*** 3297,3302 ****
--- 3288,3294 ----
rtx x;
rtx insn;
{
+ USES_PATTERN_MATCHING;
enum rtx_code code;
enum machine_mode mode;
const char *fmt;
*************** fold_rtx (x, insn)
*** 3447,3474 ****
for (; elt; elt = elt->next_same_value)
{
enum rtx_code eltcode = GET_CODE (elt->exp);
/* Just check for unary and binary operations. */
! if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1'
! && GET_CODE (elt->exp) != SIGN_EXTEND
! && GET_CODE (elt->exp) != ZERO_EXTEND
! && GET_CODE (XEXP (elt->exp, 0)) == SUBREG
! && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode
&& (GET_MODE_CLASS (mode)
! == GET_MODE_CLASS (GET_MODE (XEXP (elt->exp, 0)))))
{
- rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
-
if (GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
op0 = equiv_constant (op0);
if (op0)
! new = simplify_unary_operation (GET_CODE (elt->exp), mode,
! op0, mode);
}
! else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2'
! || GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c')
&& eltcode != DIV && eltcode != MOD
&& eltcode != UDIV && eltcode != UMOD
&& eltcode != ASHIFTRT && eltcode != LSHIFTRT
--- 3439,3464 ----
for (; elt; elt = elt->next_same_value)
{
enum rtx_code eltcode = GET_CODE (elt->exp);
+ rtx op0 = NULL_RTX, op1;
/* Just check for unary and binary operations. */
! if (GET_RTX_CLASS (eltcode) == '1'
! && MATCH_RTX ("(!{zero_extend | sign_extend} \
! (subreg $op0)@1)",
! elt->exp)
! && GET_MODE (op0) == mode
&& (GET_MODE_CLASS (mode)
! == GET_MODE_CLASS (GET_MODE (_match1))))
{
if (GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
op0 = equiv_constant (op0);
if (op0)
! new = simplify_unary_operation (eltcode, mode, op0, mode);
}
! else if ((GET_RTX_CLASS (eltcode) == '2'
! || GET_RTX_CLASS (eltcode) == 'c')
&& eltcode != DIV && eltcode != MOD
&& eltcode != UDIV && eltcode != UMOD
&& eltcode != ASHIFTRT && eltcode != LSHIFTRT
*************** fold_rtx (x, insn)
*** 3482,3489 ****
== mode))
|| CONSTANT_P (XEXP (elt->exp, 1))))
{
! rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
! rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
--- 3472,3479 ----
== mode))
|| CONSTANT_P (XEXP (elt->exp, 1))))
{
! op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
! op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
*************** fold_rtx (x, insn)
*** 3523,3534 ****
op0, op1);
}
! else if (GET_CODE (elt->exp) == SUBREG
! && GET_MODE (SUBREG_REG (elt->exp)) == mode
&& (GET_MODE_SIZE (GET_MODE (folded_arg0))
<= UNITS_PER_WORD)
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
! new = copy_rtx (SUBREG_REG (elt->exp));
if (new)
return new;
--- 3513,3523 ----
op0, op1);
}
! else if (MATCH_RTX ("(subreg $op0/mode == GET_MODE/)", elt->exp)
&& (GET_MODE_SIZE (GET_MODE (folded_arg0))
<= UNITS_PER_WORD)
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
! new = copy_rtx (op0);
if (new)
return new;
*************** fold_rtx (x, insn)
*** 3557,3563 ****
/* Even if we don't fold in the insn itself,
we can safely do so here, in hopes of getting a constant. */
rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
! rtx base = 0;
HOST_WIDE_INT offset = 0;
if (GET_CODE (addr) == REG
--- 3546,3552 ----
/* Even if we don't fold in the insn itself,
we can safely do so here, in hopes of getting a constant. */
rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
! rtx base = NULL_RTX, label = NULL_RTX;
HOST_WIDE_INT offset = 0;
if (GET_CODE (addr) == REG
*************** fold_rtx (x, insn)
*** 3572,3590 ****
}
/* If address is constant, split it into a base and integer offset. */
! if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
base = addr;
! else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
! && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
! {
! base = XEXP (XEXP (addr, 0), 0);
! offset = INTVAL (XEXP (XEXP (addr, 0), 1));
! }
! else if (GET_CODE (addr) == LO_SUM
! && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
! base = XEXP (addr, 1);
! else if (GET_CODE (addr) == ADDRESSOF)
return change_address (x, VOIDmode, addr);
/* If this is a constant pool reference, we can fold it into its
constant to allow better value tracking. */
--- 3561,3576 ----
}
/* If address is constant, split it into a base and integer offset. */
! if (MATCH_RTX ("({symbol_ref | label_ref})", addr))
base = addr;
! else if (MATCH_RTX ("(const (plus $base const_int@1))", addr))
! offset = INTVAL (_match1);
! else if (MATCH_RTX ("(lo_sum _ symbol_ref@base)", addr))
! ;
! else if (MATCH_RTX ("addressof", addr))
return change_address (x, VOIDmode, addr);
+ else
+ base = NULL_RTX;
/* If this is a constant pool reference, we can fold it into its
constant to allow better value tracking. */
*************** fold_rtx (x, insn)
*** 3627,3635 ****
/* If this is a reference to a label at a known position in a jump
table, we also know its value. */
! if (base && GET_CODE (base) == LABEL_REF)
{
- rtx label = XEXP (base, 0);
rtx table_insn = NEXT_INSN (label);
if (table_insn && GET_CODE (table_insn) == JUMP_INSN
--- 3613,3620 ----
/* If this is a reference to a label at a known position in a jump
table, we also know its value. */
! if (base && MATCH_RTX ("(label_ref $label)", base))
{
rtx table_insn = NEXT_INSN (label);
if (table_insn && GET_CODE (table_insn) == JUMP_INSN
*************** fold_rtx (x, insn)
*** 4091,4133 ****
with that LABEL_REF as its second operand. If so, the result is
the first operand of that MINUS. This handles switches with an
ADDR_DIFF_VEC table. */
! if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg0) == MINUS ? folded_arg0
: lookup_as_function (folded_arg0, MINUS);
! if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
! && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
! return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
: lookup_as_function (folded_arg0, CONST))) != 0
! && GET_CODE (XEXP (y, 0)) == MINUS
! && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
! && XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg1, 0))
! return XEXP (XEXP (y, 0), 0);
}
/* Likewise if the operands are in the other order. */
! if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg1) == MINUS ? folded_arg1
: lookup_as_function (folded_arg1, MINUS);
! if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
! && XEXP (XEXP (y, 1), 0) == XEXP (const_arg0, 0))
! return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
: lookup_as_function (folded_arg1, CONST))) != 0
! && GET_CODE (XEXP (y, 0)) == MINUS
! && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
! && XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg0, 0))
! return XEXP (XEXP (y, 0), 0);
}
/* If second operand is a register equivalent to a negative
--- 4076,4118 ----
with that LABEL_REF as its second operand. If so, the result is
the first operand of that MINUS. This handles switches with an
ADDR_DIFF_VEC table. */
! if (const_arg1 && MATCH_RTX ("(label_ref $9)", const_arg1))
{
rtx y
= GET_CODE (folded_arg0) == MINUS ? folded_arg0
: lookup_as_function (folded_arg0, MINUS);
! if (y != 0
! && MATCH_RTX ("(* $0 (label_ref _/(_match9 == _loc)/))", y))
! return _match0;
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
: lookup_as_function (folded_arg0, CONST))) != 0
! && MATCH_RTX ("(* (minus $0 \
! (label_ref _/(_match9 == _loc)/)))",
! y))
! return _match0;
}
/* Likewise if the operands are in the other order. */
! if (const_arg0 && MATCH_RTX ("(label_ref $9)", const_arg0))
{
rtx y
= GET_CODE (folded_arg1) == MINUS ? folded_arg1
: lookup_as_function (folded_arg1, MINUS);
! if (y != 0
! && MATCH_RTX ("(* $0 (label_ref _/(_match9 == _loc)/))", y))
! return _match0;
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
: lookup_as_function (folded_arg1, CONST))) != 0
! && MATCH_RTX ("(* (minus $0 \
! (label_ref _/(_match9 == _loc)/)))",
! y))
! return _match0;
}
/* If second operand is a register equivalent to a negative
*************** fold_rtx (x, insn)
*** 4172,4178 ****
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
! if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
return fold_rtx (plus_constant (copy_rtx (y),
-INTVAL (const_arg1)),
NULL_RTX);
--- 4157,4163 ----
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
! if (y && MATCH_RTX ("(* _ const_int)", y))
return fold_rtx (plus_constant (copy_rtx (y),
-INTVAL (const_arg1)),
NULL_RTX);
*************** fold_rtx (x, insn)
*** 4291,4298 ****
case 'o':
/* (lo_sum (high X) X) is simply X. */
if (code == LO_SUM && const_arg0 != 0
! && GET_CODE (const_arg0) == HIGH
! && rtx_equal_p (XEXP (const_arg0, 0), const_arg1))
return const_arg1;
break;
--- 4276,4282 ----
case 'o':
/* (lo_sum (high X) X) is simply X. */
if (code == LO_SUM && const_arg0 != 0
! && MATCH_RTX ("(high [const_arg1])", const_arg0))
return const_arg1;
break;
*************** cse_insn (insn, libcall_insn)
*** 4731,4736 ****
--- 4715,4721 ----
rtx insn;
rtx libcall_insn;
{
+ USES_PATTERN_MATCHING;
rtx x = PATTERN (insn);
int i;
rtx tem;
*************** cse_insn (insn, libcall_insn)
*** 4761,4768 ****
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
{
! if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
! invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
}
}
--- 4746,4753 ----
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
{
! if (MATCH_RTX ("(* clobber@0)", tem))
! invalidate (SET_DEST (_match0), VOIDmode);
XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
}
}
*************** cse_insn (insn, libcall_insn)
*** 4824,4835 ****
{
rtx clobbered = XEXP (y, 0);
! if (GET_CODE (clobbered) == REG
! || GET_CODE (clobbered) == SUBREG)
invalidate (clobbered, VOIDmode);
! else if (GET_CODE (clobbered) == STRICT_LOW_PART
! || GET_CODE (clobbered) == ZERO_EXTRACT)
! invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
}
}
--- 4809,4819 ----
{
rtx clobbered = XEXP (y, 0);
! if (MATCH_RTX ("({reg | subreg})", clobbered))
invalidate (clobbered, VOIDmode);
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)",
! clobbered))
! invalidate (_match0, GET_MODE (clobbered));
}
}
*************** cse_insn (insn, libcall_insn)
*** 4858,4865 ****
/* If we clobber memory, canon the address.
This does nothing when a register is clobbered
because we have already invalidated the reg. */
! if (GET_CODE (XEXP (y, 0)) == MEM)
! canon_reg (XEXP (y, 0), NULL_RTX);
}
else if (GET_CODE (y) == USE
&& ! (GET_CODE (XEXP (y, 0)) == REG
--- 4842,4849 ----
/* If we clobber memory, canon the address.
This does nothing when a register is clobbered
because we have already invalidated the reg. */
! if (MATCH_RTX ("(clobber mem@0)", y))
! canon_reg (_match0, NULL_RTX);
}
else if (GET_CODE (y) == USE
&& ! (GET_CODE (XEXP (y, 0)) == REG
*************** cse_insn (insn, libcall_insn)
*** 4877,4884 ****
}
else if (GET_CODE (x) == CLOBBER)
{
! if (GET_CODE (XEXP (x, 0)) == MEM)
! canon_reg (XEXP (x, 0), NULL_RTX);
}
/* Canonicalize a USE of a pseudo register or memory location. */
--- 4861,4868 ----
}
else if (GET_CODE (x) == CLOBBER)
{
! if (MATCH_RTX ("(clobber mem@0)", x))
! canon_reg (_match0, NULL_RTX);
}
/* Canonicalize a USE of a pseudo register or memory location. */
*************** cse_insn (insn, libcall_insn)
*** 4934,4951 ****
else
SET_SRC (sets[i].rtl) = new;
! if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
validate_change (insn, &XEXP (dest, 1),
! canon_reg (XEXP (dest, 1), insn), 1);
validate_change (insn, &XEXP (dest, 2),
! canon_reg (XEXP (dest, 2), insn), 1);
}
! while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
! || GET_CODE (dest) == ZERO_EXTRACT
! || GET_CODE (dest) == SIGN_EXTRACT)
! dest = XEXP (dest, 0);
if (GET_CODE (dest) == MEM)
canon_reg (dest, insn);
--- 4918,4934 ----
else
SET_SRC (sets[i].rtl) = new;
! if (MATCH_RTX ("({zero_extract | sign_extract} _ $1 $2)", dest))
{
validate_change (insn, &XEXP (dest, 1),
! canon_reg (_match1, insn), 1);
validate_change (insn, &XEXP (dest, 2),
! canon_reg (_match2, insn), 1);
}
! while (MATCH_RTX ("({subreg | strict_low_part \
! | zero_extract | sign_extract} $1)", dest))
! dest = _match1;
if (GET_CODE (dest) == MEM)
canon_reg (dest, insn);
*************** cse_insn (insn, libcall_insn)
*** 5005,5012 ****
if (src_eqv)
{
enum machine_mode eqvmode = mode;
! if (GET_CODE (dest) == STRICT_LOW_PART)
! eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
do_not_record = 0;
hash_arg_in_memory = 0;
src_eqv_hash = HASH (src_eqv, eqvmode);
--- 4988,4995 ----
if (src_eqv)
{
enum machine_mode eqvmode = mode;
! if (MATCH_RTX ("(strict_low_part (subreg $0))", dest))
! eqvmode = GET_MODE (_match0);
do_not_record = 0;
hash_arg_in_memory = 0;
src_eqv_hash = HASH (src_eqv, eqvmode);
*************** cse_insn (insn, libcall_insn)
*** 5150,5158 ****
/* Consider (minus (label_ref L1) (label_ref L2)) as
"constant" here so we will record it. This allows us
to fold switch statements when an ADDR_DIFF_VEC is used. */
! || (GET_CODE (src_folded) == MINUS
! && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF
! && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF)))
src_const = src_folded, src_const_elt = elt;
else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
src_const = src_eqv_here, src_const_elt = src_eqv_elt;
--- 5133,5139 ----
/* Consider (minus (label_ref L1) (label_ref L2)) as
"constant" here so we will record it. This allows us
to fold switch statements when an ADDR_DIFF_VEC is used. */
! || MATCH_RTX ("(minus label_ref label_ref)", src_folded)))
src_const = src_folded, src_const_elt = elt;
else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
src_const = src_eqv_here, src_const_elt = src_eqv_elt;
*************** cse_insn (insn, libcall_insn)
*** 5243,5259 ****
value. */
if (flag_expensive_optimizations && ! src_related
! && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
enum machine_mode tmode;
! rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
! rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0));
struct table_elt *larger_elt;
if (inner)
--- 5224,5240 ----
value. */
if (flag_expensive_optimizations && ! src_related
! && MATCH_RTX ("(and $0 const_int@1)", src)
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
enum machine_mode tmode;
! rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, _match1);
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
! rtx inner = gen_lowpart_if_possible (tmode, _match0);
struct table_elt *larger_elt;
if (inner)
*************** cse_insn (insn, libcall_insn)
*** 5567,5579 ****
/* Reject cases that will abort in decode_rtx_const.
On the alpha when simplifying a switch, we get
(const (truncate (minus (label_ref) (label_ref)))). */
! && ! (GET_CODE (trial) == CONST
! && GET_CODE (XEXP (trial, 0)) == TRUNCATE)
! /* Likewise on IA-64, except without the truncate. */
! && ! (GET_CODE (trial) == CONST
! && GET_CODE (XEXP (trial, 0)) == MINUS
! && GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF
! && GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF)
&& (src_folded == 0
|| (GET_CODE (src_folded) != MEM
&& ! src_folded_force_flag))
--- 5548,5555 ----
/* Reject cases that will abort in decode_rtx_const.
On the alpha when simplifying a switch, we get
(const (truncate (minus (label_ref) (label_ref)))). */
! && !MATCH_RTX ("(const truncate)", trial)
! && !MATCH_RTX ("(const (minus label_ref label_ref))", trial)
&& (src_folded == 0
|| (GET_CODE (src_folded) != MEM
&& ! src_folded_force_flag))
*************** cse_insn (insn, libcall_insn)
*** 5659,5669 ****
entry in a jump table. */
if (n_sets == 1 && src_const && GET_CODE (dest) == REG
! && GET_CODE (src_const) != REG
! && ! (GET_CODE (src_const) == CONST
! && GET_CODE (XEXP (src_const, 0)) == MINUS
! && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
! && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
{
/* We only want a REG_EQUAL note if src_const != src. */
if (! rtx_equal_p (src, src_const))
--- 5635,5642 ----
entry in a jump table. */
if (n_sets == 1 && src_const && GET_CODE (dest) == REG
! && ! MATCH_RTX ("reg", src_const)
! && ! MATCH_RTX ("(const (minus label_ref label_ref))", src_const))
{
/* We only want a REG_EQUAL note if src_const != src. */
if (! rtx_equal_p (src, src_const))
*************** cse_insn (insn, libcall_insn)
*** 5840,5848 ****
if (! libcall_insn || insn == libcall_insn)
invalidate (dest, VOIDmode);
}
! else if (GET_CODE (dest) == STRICT_LOW_PART
! || GET_CODE (dest) == ZERO_EXTRACT)
! invalidate (XEXP (dest, 0), GET_MODE (dest));
sets[i].rtl = 0;
}
--- 5813,5820 ----
if (! libcall_insn || insn == libcall_insn)
invalidate (dest, VOIDmode);
}
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", dest))
! invalidate (_match0, GET_MODE (dest));
sets[i].rtl = 0;
}
*************** cse_insn (insn, libcall_insn)
*** 6003,6011 ****
if (! libcall_insn || insn == libcall_insn)
invalidate (dest, VOIDmode);
}
! else if (GET_CODE (dest) == STRICT_LOW_PART
! || GET_CODE (dest) == ZERO_EXTRACT)
! invalidate (XEXP (dest, 0), GET_MODE (dest));
}
/* A volatile ASM invalidates everything. */
--- 5975,5982 ----
if (! libcall_insn || insn == libcall_insn)
invalidate (dest, VOIDmode);
}
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", dest))
! invalidate (_match0, GET_MODE (dest));
}
/* A volatile ASM invalidates everything. */
*************** cse_insn (insn, libcall_insn)
*** 6125,6134 ****
/* STRICT_LOW_PART isn't part of the value BEING set,
and neither is the SUBREG inside it.
Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */
! if (GET_CODE (dest) == STRICT_LOW_PART)
! dest = SUBREG_REG (XEXP (dest, 0));
! if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
/* Registers must also be inserted into chains for quantities. */
if (insert_regs (dest, sets[i].src_elt, 1))
{
--- 6096,6105 ----
/* STRICT_LOW_PART isn't part of the value BEING set,
and neither is the SUBREG inside it.
Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */
! if (MATCH_RTX ("(strict_low_part (subreg $0))", dest))
! dest = _match0;
! if (MATCH_RTX ("({reg | subreg})", dest))
/* Registers must also be inserted into chains for quantities. */
if (insert_regs (dest, sets[i].src_elt, 1))
{
*************** cse_insn (insn, libcall_insn)
*** 6138,6145 ****
sets[i].dest_hash = HASH (dest, GET_MODE (dest));
}
! if (GET_CODE (inner_dest) == MEM
! && GET_CODE (XEXP (inner_dest, 0)) == ADDRESSOF)
/* Given (SET (MEM (ADDRESSOF (X))) Y) we don't want to say
that (MEM (ADDRESSOF (X))) is equivalent to Y.
Consider the case in which the address of the MEM is
--- 6109,6115 ----
sets[i].dest_hash = HASH (dest, GET_MODE (dest));
}
! if (MATCH_RTX ("(mem addressof)", inner_dest))
/* Given (SET (MEM (ADDRESSOF (X))) Y) we don't want to say
that (MEM (ADDRESSOF (X))) is equivalent to Y.
Consider the case in which the address of the MEM is
*************** static int
*** 6391,6399 ****
addr_affects_sp_p (addr)
rtx addr;
{
if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
! && GET_CODE (XEXP (addr, 0)) == REG
! && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
{
if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
{
--- 6361,6370 ----
addr_affects_sp_p (addr)
rtx addr;
{
+ USES_PATTERN_MATCHING;
+
if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
! && MATCH_RTX ("(* reg/STACK_POINTER_REGNUM == REGNO/)", addr))
{
if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
{
*************** static void
*** 6423,6439 ****
invalidate_from_clobbers (x)
rtx x;
{
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
if (ref)
{
! if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
! || GET_CODE (ref) == MEM)
invalidate (ref, VOIDmode);
! else if (GET_CODE (ref) == STRICT_LOW_PART
! || GET_CODE (ref) == ZERO_EXTRACT)
! invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
else if (GET_CODE (x) == PARALLEL)
--- 6394,6410 ----
invalidate_from_clobbers (x)
rtx x;
{
+ USES_PATTERN_MATCHING;
+
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
if (ref)
{
! if (MATCH_RTX ("({reg | subreg | mem})", ref))
invalidate (ref, VOIDmode);
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", ref))
! invalidate (_match0, GET_MODE (ref));
}
}
else if (GET_CODE (x) == PARALLEL)
*************** invalidate_from_clobbers (x)
*** 6445,6456 ****
if (GET_CODE (y) == CLOBBER)
{
rtx ref = XEXP (y, 0);
! if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
! || GET_CODE (ref) == MEM)
invalidate (ref, VOIDmode);
! else if (GET_CODE (ref) == STRICT_LOW_PART
! || GET_CODE (ref) == ZERO_EXTRACT)
! invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
}
--- 6416,6425 ----
if (GET_CODE (y) == CLOBBER)
{
rtx ref = XEXP (y, 0);
! if (MATCH_RTX ("({reg | subreg | mem})", ref))
invalidate (ref, VOIDmode);
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", ref))
! invalidate (_match0, GET_MODE (ref));
}
}
}
*************** static void
*** 6563,6568 ****
--- 6532,6538 ----
cse_around_loop (loop_start)
rtx loop_start;
{
+ USES_PATTERN_MATCHING;
rtx insn;
int i;
struct table_elt *p;
*************** cse_around_loop (loop_start)
*** 6587,6599 ****
for (p = last_jump_equiv_class->first_same_value; p;
p = p->next_same_value)
{
! if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
! || (GET_CODE (p->exp) == SUBREG
! && GET_CODE (SUBREG_REG (p->exp)) == REG))
invalidate (p->exp, VOIDmode);
! else if (GET_CODE (p->exp) == STRICT_LOW_PART
! || GET_CODE (p->exp) == ZERO_EXTRACT)
! invalidate (XEXP (p->exp, 0), GET_MODE (p->exp));
}
/* Process insns starting after LOOP_START until we hit a CALL_INSN or
--- 6557,6566 ----
for (p = last_jump_equiv_class->first_same_value; p;
p = p->next_same_value)
{
! if (MATCH_RTX ("{({reg | mem}) | (subreg reg)}", p->exp))
invalidate (p->exp, VOIDmode);
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", p->exp))
! invalidate (_match0, GET_MODE (p->exp));
}
/* Process insns starting after LOOP_START until we hit a CALL_INSN or
*************** cse_around_loop (loop_start)
*** 6616,6628 ****
insn = NEXT_INSN (insn))
{
if (INSN_P (insn)
! && (GET_CODE (PATTERN (insn)) == SET
! || GET_CODE (PATTERN (insn)) == CLOBBER))
cse_set_around_loop (PATTERN (insn), insn, loop_start);
else if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
! if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
! || GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
loop_start);
}
--- 6583,6593 ----
insn = NEXT_INSN (insn))
{
if (INSN_P (insn)
! && MATCH_RTX ("({set|clobber})", PATTERN (insn)))
cse_set_around_loop (PATTERN (insn), insn, loop_start);
else if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
! if (MATCH_RTX ("({set|clobber})", XVECEXP (PATTERN (insn), 0, i)))
cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
loop_start);
}
*************** invalidate_skipped_set (dest, set, data)
*** 6637,6642 ****
--- 6602,6608 ----
rtx dest;
void *data ATTRIBUTE_UNUSED;
{
+ USES_PATTERN_MATCHING;
enum rtx_code code = GET_CODE (dest);
if (code == MEM
*************** invalidate_skipped_set (dest, set, data)
*** 6658,6667 ****
|| dest == pc_rtx)
return;
! if (code == STRICT_LOW_PART || code == ZERO_EXTRACT)
! invalidate (XEXP (dest, 0), GET_MODE (dest));
! else if (code == REG || code == SUBREG || code == MEM)
invalidate (dest, VOIDmode);
}
/* Invalidate all insns from START up to the end of the function or the
--- 6624,6633 ----
|| dest == pc_rtx)
return;
! if (MATCH_RTX ("({reg | subreg | mem})", dest))
invalidate (dest, VOIDmode);
+ else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", dest))
+ invalidate (_match0, GET_MODE (dest));
}
/* Invalidate all insns from START up to the end of the function or the
*************** cse_set_around_loop (x, insn, loop_start
*** 6736,6741 ****
--- 6702,6708 ----
rtx insn;
rtx loop_start;
{
+ USES_PATTERN_MATCHING;
struct table_elt *src_elt;
/* If this is a SET, see if we can replace SET_SRC, but ignore SETs that
*************** cse_set_around_loop (x, insn, loop_start
*** 6818,6829 ****
/* See comment on similar code in cse_insn for explanation of these
tests. */
! if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
! || GET_CODE (SET_DEST (x)) == MEM)
invalidate (SET_DEST (x), VOIDmode);
! else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
! || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
! invalidate (XEXP (SET_DEST (x), 0), GET_MODE (SET_DEST (x)));
}
/* Find the end of INSN's basic block and return its range,
--- 6785,6794 ----
/* See comment on similar code in cse_insn for explanation of these
tests. */
! if (MATCH_RTX ("({reg | subreg | mem})", SET_DEST (x)))
invalidate (SET_DEST (x), VOIDmode);
! else if (MATCH_RTX ("({strict_low_part | zero_extract} $0)", SET_DEST (x)))
! invalidate (_match0, GET_MODE (SET_DEST (x)));
}
/* Find the end of INSN's basic block and return its range,
*************** check_for_label_ref (rtl, data)
*** 7433,7449 ****
rtx *rtl;
void *data;
{
rtx insn = (rtx) data;
/* If this insn uses a LABEL_REF and there isn't a REG_LABEL note for it,
we must rerun jump since it needs to place the note. If this is a
LABEL_REF for a CODE_LABEL that isn't in the insn chain, don't do this
since no REG_LABEL will be added. */
! return (GET_CODE (*rtl) == LABEL_REF
&& ! LABEL_REF_NONLOCAL_P (*rtl)
! && LABEL_P (XEXP (*rtl, 0))
! && INSN_UID (XEXP (*rtl, 0)) != 0
! && ! find_reg_note (insn, REG_LABEL, XEXP (*rtl, 0)));
}
/* Count the number of times registers are used (not set) in X.
--- 7398,7415 ----
rtx *rtl;
void *data;
{
+ USES_PATTERN_MATCHING;
rtx insn = (rtx) data;
/* If this insn uses a LABEL_REF and there isn't a REG_LABEL note for it,
we must rerun jump since it needs to place the note. If this is a
LABEL_REF for a CODE_LABEL that isn't in the insn chain, don't do this
since no REG_LABEL will be added. */
! return (MATCH_RTX ("(label_ref $0)", *rtl)
&& ! LABEL_REF_NONLOCAL_P (*rtl)
! && LABEL_P (_match0)
! && INSN_UID (_match0) != 0
! && ! find_reg_note (insn, REG_LABEL, _match0));
}
/* Count the number of times registers are used (not set) in X.
*************** count_reg_usage (x, counts, dest, incr)
*** 7461,7466 ****
--- 7427,7433 ----
rtx dest;
int incr;
{
+ USES_PATTERN_MATCHING;
enum rtx_code code;
rtx note;
const char *fmt;
*************** count_reg_usage (x, counts, dest, incr)
*** 7489,7496 ****
case CLOBBER:
/* If we are clobbering a MEM, mark any registers inside the address
as being used. */
! if (GET_CODE (XEXP (x, 0)) == MEM)
! count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
return;
case SET:
--- 7456,7463 ----
case CLOBBER:
/* If we are clobbering a MEM, mark any registers inside the address
as being used. */
! if (MATCH_RTX ("(clobber (mem $0))", x))
! count_reg_usage (_match0, counts, NULL_RTX, incr);
return;
case SET:
Index: doc/gccint.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/gccint.texi,v
retrieving revision 1.10
diff -c -3 -p -r1.10 gccint.texi
*** doc/gccint.texi 15 Mar 2003 19:54:10 -0000 1.10
--- doc/gccint.texi 18 Jun 2003 21:37:32 -0000
*************** Additional tutorial information is linke
*** 153,158 ****
--- 153,159 ----
* Collect2:: How @code{collect2} works; how it finds @code{ld}.
* Header Dirs:: Understanding the standard header file directories.
* Type Information:: GCC's memory management; generating type information.
+ * RTL Pattern Matching:: The facility for easier pattern matching on RTL.
* Funding:: How to help assure funding for free software.
* GNU Project:: The GNU Project and GNU/Linux.
*************** Additional tutorial information is linke
*** 182,187 ****
--- 183,189 ----
@include collect2.texi
@include headerdirs.texi
@include gty.texi
+ @include match.texi
@include funding.texi
@include gnu.texi