/* Generate pattern matching and transform code shared between
GENERIC and GIMPLE folding code from match-and-simplify description.
- Copyright (C) 2014-2015 Free Software Foundation, Inc.
+ Copyright (C) 2014-2019 Free Software Foundation, Inc.
Contributed by Richard Biener <rguenther@suse.de>
and Prathamesh Kulkarni <bilbotheelffriend@gmail.com>
<http://www.gnu.org/licenses/>. */
#include "bconfig.h"
-#include <new>
#include "system.h"
#include "coretypes.h"
#include <cpplib.h>
static struct line_maps *line_table;
+/* The rich_location class within libcpp requires a way to expand
+ location_t instances, and relies on the client code
+ providing a symbol named
+ linemap_client_expand_location_to_spelling_point
+ to do this.
+
+ This is the implementation for genmatch. */
+
+expanded_location
+linemap_client_expand_location_to_spelling_point (location_t loc,
+ enum location_aspect)
+{
+ const struct line_map_ordinary *map;
+ loc = linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, &map);
+ return linemap_expand_location (line_table, map, loc);
+}
+
static bool
#if GCC_VERSION >= 4001
-__attribute__((format (printf, 6, 0)))
+__attribute__((format (printf, 5, 0)))
#endif
-error_cb (cpp_reader *, int errtype, int, source_location location,
- unsigned int, const char *msg, va_list *ap)
+diagnostic_cb (cpp_reader *, enum cpp_diagnostic_level errtype,
+ enum cpp_warning_reason, rich_location *richloc,
+ const char *msg, va_list *ap)
{
const line_map_ordinary *map;
+ location_t location = richloc->get_loc ();
linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map);
expanded_location loc = linemap_expand_location (line_table, map, location);
fprintf (stderr, "%s:%d:%d %s: ", loc.file, loc.line, loc.column,
#endif
fatal_at (const cpp_token *tk, const char *msg, ...)
{
+ rich_location richloc (line_table, tk->src_loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_FATAL, 0, tk->src_loc, 0, msg, &ap);
+ diagnostic_cb (NULL, CPP_DL_FATAL, CPP_W_NONE, &richloc, msg, &ap);
va_end (ap);
}
#if GCC_VERSION >= 4001
__attribute__((format (printf, 2, 3)))
#endif
-fatal_at (source_location loc, const char *msg, ...)
+fatal_at (location_t loc, const char *msg, ...)
{
+ rich_location richloc (line_table, loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_FATAL, 0, loc, 0, msg, &ap);
+ diagnostic_cb (NULL, CPP_DL_FATAL, CPP_W_NONE, &richloc, msg, &ap);
va_end (ap);
}
#endif
warning_at (const cpp_token *tk, const char *msg, ...)
{
+ rich_location richloc (line_table, tk->src_loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_WARNING, 0, tk->src_loc, 0, msg, &ap);
+ diagnostic_cb (NULL, CPP_DL_WARNING, CPP_W_NONE, &richloc, msg, &ap);
va_end (ap);
}
#if GCC_VERSION >= 4001
__attribute__((format (printf, 2, 3)))
#endif
-warning_at (source_location loc, const char *msg, ...)
+warning_at (location_t loc, const char *msg, ...)
{
+ rich_location richloc (line_table, loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_WARNING, 0, loc, 0, msg, &ap);
+ diagnostic_cb (NULL, CPP_DL_WARNING, CPP_W_NONE, &richloc, msg, &ap);
va_end (ap);
}
}
static void
-output_line_directive (FILE *f, source_location location,
- bool dumpfile = false)
+output_line_directive (FILE *f, location_t location,
+ bool dumpfile = false, bool fnargs = false)
{
const line_map_ordinary *map;
linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map);
{
/* When writing to a dumpfile only dump the filename. */
const char *file = strrchr (loc.file, DIR_SEPARATOR);
+#if defined(DIR_SEPARATOR_2)
+ const char *pos2 = strrchr (loc.file, DIR_SEPARATOR_2);
+ if (pos2 && (!file || (pos2 > file)))
+ file = pos2;
+#endif
if (!file)
file = loc.file;
else
++file;
- fprintf (f, "%s:%d", file, loc.line);
+
+ if (fnargs)
+ fprintf (f, "\"%s\", %d", file, loc.line);
+ else
+ fprintf (f, "%s:%d", file, loc.line);
}
else
/* Other gen programs really output line directives here, at least for
#include "builtins.def"
END_BUILTINS
};
-#undef DEF_BUILTIN
+
+#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) IFN_##CODE,
+enum internal_fn {
+#include "internal-fn.def"
+ IFN_LAST
+};
+
+enum combined_fn {
+#define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) \
+ CFN_##ENUM = int (ENUM),
+#include "builtins.def"
+
+#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) \
+ CFN_##CODE = int (END_BUILTINS) + int (IFN_##CODE),
+#include "internal-fn.def"
+
+ CFN_LAST
+};
+
+#include "case-cfn-macros.h"
/* Return true if CODE represents a commutative tree code. Otherwise
return false. */
case WIDEN_MULT_PLUS_EXPR:
case WIDEN_MULT_MINUS_EXPR:
case DOT_PROD_EXPR:
- case FMA_EXPR:
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Return true if CODE is a comparison. */
+
+bool
+comparison_code_p (enum tree_code code)
+{
+ switch (code)
+ {
+ case EQ_EXPR:
+ case NE_EXPR:
+ case ORDERED_EXPR:
+ case UNORDERED_EXPR:
+ case LTGT_EXPR:
+ case UNEQ_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
return true;
default:
struct id_base : nofree_ptr_hash<id_base>
{
- enum id_kind { CODE, FN, PREDICATE, USER } kind;
+ enum id_kind { CODE, FN, PREDICATE, USER, NULL_ID } kind;
id_base (id_kind, const char *, int = -1);
&& strcmp (op1->id, op2->id) == 0);
}
+/* The special id "null", which matches nothing. */
+static id_base *null_id;
+
/* Hashtable of known pattern operators. This is pre-seeded from
all known tree codes and all known builtin function ids. */
static hash_table<id_base> *operators;
const char *tcc;
};
-/* Identifier that maps to a builtin function code. */
+/* Identifier that maps to a builtin or internal function code. */
struct fn_id : public id_base
{
fn_id (enum built_in_function fn_, const char *id_)
: id_base (id_base::FN, id_), fn (fn_) {}
- enum built_in_function fn;
+ fn_id (enum internal_fn fn_, const char *id_)
+ : id_base (id_base::FN, id_), fn (int (END_BUILTINS) + int (fn_)) {}
+ unsigned int fn;
};
struct simplify;
return id->kind == id_base::USER;
}
+/* If ID has a pair of consecutive, commutative operands, return the
+ index of the first, otherwise return -1. */
+
+static int
+commutative_op (id_base *id)
+{
+ if (operator_id *code = dyn_cast <operator_id *> (id))
+ {
+ if (commutative_tree_code (code->code)
+ || commutative_ternary_tree_code (code->code))
+ return 0;
+ return -1;
+ }
+ if (fn_id *fn = dyn_cast <fn_id *> (id))
+ switch (fn->fn)
+ {
+ CASE_CFN_FMA:
+ case CFN_FMS:
+ case CFN_FNMA:
+ case CFN_FNMS:
+ return 0;
+
+ default:
+ return -1;
+ }
+ if (user_id *uid = dyn_cast<user_id *> (id))
+ {
+ int res = commutative_op (uid->substitutes[0]);
+ if (res < 0)
+ return 0;
+ for (unsigned i = 1; i < uid->substitutes.length (); ++i)
+ if (res != commutative_op (uid->substitutes[i]))
+ return -1;
+ return res;
+ }
+ return -1;
+}
+
/* Add a predicate identifier to the hash. */
static predicate_id *
*slot = op;
}
-/* Add a builtin identifier to the hash. */
+/* Add a built-in or internal function identifier to the hash. ID is
+ the name of its CFN_* enumeration value. */
+template <typename T>
static void
-add_builtin (enum built_in_function code, const char *id)
+add_function (T code, const char *id)
{
fn_id *fn = new fn_id (code, id);
id_base **slot = operators->find_slot_with_hash (fn, fn->hashval, INSERT);
return false;
}
-/* Lookup the identifier ID. */
+/* Lookup the identifier ID. Allow "null" if ALLOW_NULL. */
id_base *
-get_operator (const char *id)
+get_operator (const char *id, bool allow_null = false)
{
+ if (allow_null && strcmp (id, "null") == 0)
+ return null_id;
+
id_base tem (id_base::CODE, id);
id_base *op = operators->find_with_hash (&tem, tem.hashval);
return op;
}
- /* Try all-uppercase. */
- char *id2 = xstrdup (id);
- for (unsigned i = 0; i < strlen (id2); ++i)
- id2[i] = TOUPPER (id2[i]);
- new (&tem) id_base (id_base::CODE, id2);
- op = operators->find_with_hash (&tem, tem.hashval);
- if (op)
+ char *id2;
+ bool all_upper = true;
+ bool all_lower = true;
+ for (unsigned int i = 0; id[i]; ++i)
+ if (ISUPPER (id[i]))
+ all_lower = false;
+ else if (ISLOWER (id[i]))
+ all_upper = false;
+ if (all_lower)
{
- free (id2);
- return op;
+ /* Try in caps with _EXPR appended. */
+ id2 = ACONCAT ((id, "_EXPR", NULL));
+ for (unsigned int i = 0; id2[i]; ++i)
+ id2[i] = TOUPPER (id2[i]);
}
+ else if (all_upper && strncmp (id, "IFN_", 4) == 0)
+ /* Try CFN_ instead of IFN_. */
+ id2 = ACONCAT (("CFN_", id + 4, NULL));
+ else if (all_upper && strncmp (id, "BUILT_IN_", 9) == 0)
+ /* Try prepending CFN_. */
+ id2 = ACONCAT (("CFN_", id, NULL));
+ else
+ return NULL;
- /* Try _EXPR appended. */
- id2 = (char *)xrealloc (id2, strlen (id2) + sizeof ("_EXPR") + 1);
- strcat (id2, "_EXPR");
new (&tem) id_base (id_base::CODE, id2);
- op = operators->find_with_hash (&tem, tem.hashval);
- if (op)
+ return operators->find_with_hash (&tem, tem.hashval);
+}
+
+/* Return the comparison operators that results if the operands are
+ swapped. This is safe for floating-point. */
+
+id_base *
+swap_tree_comparison (operator_id *p)
+{
+ switch (p->code)
{
- free (id2);
- return op;
+ case EQ_EXPR:
+ case NE_EXPR:
+ case ORDERED_EXPR:
+ case UNORDERED_EXPR:
+ case LTGT_EXPR:
+ case UNEQ_EXPR:
+ return p;
+ case GT_EXPR:
+ return get_operator ("LT_EXPR");
+ case GE_EXPR:
+ return get_operator ("LE_EXPR");
+ case LT_EXPR:
+ return get_operator ("GT_EXPR");
+ case LE_EXPR:
+ return get_operator ("GE_EXPR");
+ case UNGT_EXPR:
+ return get_operator ("UNLT_EXPR");
+ case UNGE_EXPR:
+ return get_operator ("UNLE_EXPR");
+ case UNLT_EXPR:
+ return get_operator ("UNGT_EXPR");
+ case UNLE_EXPR:
+ return get_operator ("UNGE_EXPR");
+ default:
+ gcc_unreachable ();
}
-
- return 0;
}
typedef hash_map<nofree_string_hash, unsigned> cid_map_t;
struct operand {
enum op_type { OP_PREDICATE, OP_EXPR, OP_CAPTURE, OP_C_EXPR, OP_IF, OP_WITH };
- operand (enum op_type type_, source_location loc_)
+ operand (enum op_type type_, location_t loc_)
: type (type_), location (loc_) {}
enum op_type type;
- source_location location;
+ location_t location;
virtual void gen_transform (FILE *, int, const char *, bool, int,
const char *, capture_info *,
dt_operand ** = 0,
- bool = true)
+ int = 0)
{ gcc_unreachable (); }
};
struct predicate : public operand
{
- predicate (predicate_id *p_, source_location loc)
+ predicate (predicate_id *p_, location_t loc)
: operand (OP_PREDICATE, loc), p (p_) {}
predicate_id *p;
};
struct expr : public operand
{
- expr (id_base *operation_, source_location loc, bool is_commutative_ = false)
+ expr (id_base *operation_, location_t loc, bool is_commutative_ = false)
: operand (OP_EXPR, loc), operation (operation_),
ops (vNULL), expr_type (NULL), is_commutative (is_commutative_),
is_generic (false), force_single_use (false) {}
bool force_single_use;
virtual void gen_transform (FILE *f, int, const char *, bool, int,
const char *, capture_info *,
- dt_operand ** = 0, bool = true);
+ dt_operand ** = 0, int = 0);
};
/* An operator that is represented by native C code. This is always
id_tab (const char *id_, const char *oper_): id (id_), oper (oper_) {}
};
- c_expr (cpp_reader *r_, source_location loc,
+ c_expr (cpp_reader *r_, location_t loc,
vec<cpp_token> code_, unsigned nr_stmts_,
vec<id_tab> ids_, cid_map_t *capture_ids_)
: operand (OP_C_EXPR, loc), r (r_), code (code_),
vec<id_tab> ids;
virtual void gen_transform (FILE *f, int, const char *, bool, int,
const char *, capture_info *,
- dt_operand ** = 0, bool = true);
+ dt_operand ** = 0, int = 0);
};
/* A wrapper around another operand that captures its value. */
struct capture : public operand
{
- capture (source_location loc, unsigned where_, operand *what_)
- : operand (OP_CAPTURE, loc), where (where_), what (what_) {}
+ capture (location_t loc, unsigned where_, operand *what_, bool value_)
+ : operand (OP_CAPTURE, loc), where (where_), value_match (value_),
+ what (what_) {}
/* Identifier index for the value. */
unsigned where;
+ /* Whether in a match of two operands the compare should be for
+ equal values rather than equal atoms (boils down to a type
+ check or not). */
+ bool value_match;
/* The captured value. */
operand *what;
virtual void gen_transform (FILE *f, int, const char *, bool, int,
const char *, capture_info *,
- dt_operand ** = 0, bool = true);
+ dt_operand ** = 0, int = 0);
};
/* if expression. */
struct if_expr : public operand
{
- if_expr (source_location loc)
+ if_expr (location_t loc)
: operand (OP_IF, loc), cond (NULL), trueexpr (NULL), falseexpr (NULL) {}
c_expr *cond;
operand *trueexpr;
struct with_expr : public operand
{
- with_expr (source_location loc)
+ with_expr (location_t loc)
: operand (OP_WITH, loc), with (NULL), subexpr (NULL) {}
c_expr *with;
operand *subexpr;
{
enum simplify_kind { SIMPLIFY, MATCH };
- simplify (simplify_kind kind_, operand *match_, operand *result_,
- vec<vec<user_id *> > for_vec_, cid_map_t *capture_ids_)
- : kind (kind_), match (match_), result (result_),
+ simplify (simplify_kind kind_, unsigned id_, operand *match_,
+ operand *result_, vec<vec<user_id *> > for_vec_,
+ cid_map_t *capture_ids_)
+ : kind (kind_), id (id_), match (match_), result (result_),
for_vec (for_vec_), for_subst_vec (vNULL),
capture_ids (capture_ids_), capture_max (capture_ids_->elements () - 1) {}
simplify_kind kind;
+ /* ID. This is kept to easily associate related simplifies expanded
+ from the same original one. */
+ unsigned id;
/* The expression that is matched against the GENERIC or GIMPLE IL. */
operand *match;
/* For a (simplify ...) an expression with ifs and withs with the expression
{
if (capture *c = dyn_cast<capture *> (o))
{
- fprintf (f, "@%u", c->where);
if (c->what && flattened == false)
- {
- putc (':', f);
- print_operand (c->what, f, flattened);
- putc (' ', f);
- }
+ print_operand (c->what, f, flattened);
+ fprintf (f, "@%u", c->where);
}
else if (predicate *p = dyn_cast<predicate *> (o))
else if (expr *e = dyn_cast<expr *> (o))
{
- fprintf (f, "(%s", e->operation->id);
-
- if (flattened == false)
+ if (e->ops.length () == 0)
+ fprintf (f, "%s", e->operation->id);
+ else
{
- putc (' ', f);
- for (unsigned i = 0; i < e->ops.length (); ++i)
+ fprintf (f, "(%s", e->operation->id);
+
+ if (flattened == false)
{
- print_operand (e->ops[i], f, flattened);
- putc (' ', f);
+ for (unsigned i = 0; i < e->ops.length (); ++i)
+ {
+ putc (' ', f);
+ print_operand (e->ops[i], f, flattened);
+ }
}
+ putc (')', f);
}
- putc (')', f);
}
else
/* Lower OP to two operands in case it is marked as commutative. */
static vec<operand *>
-commutate (operand *op)
+commutate (operand *op, vec<vec<user_id *> > &for_vec)
{
vec<operand *> ret = vNULL;
ret.safe_push (op);
return ret;
}
- vec<operand *> v = commutate (c->what);
+ vec<operand *> v = commutate (c->what, for_vec);
for (unsigned i = 0; i < v.length (); ++i)
{
- capture *nc = new capture (c->location, c->where, v[i]);
+ capture *nc = new capture (c->location, c->where, v[i],
+ c->value_match);
ret.safe_push (nc);
}
return ret;
vec< vec<operand *> > ops_vector = vNULL;
for (unsigned i = 0; i < e->ops.length (); ++i)
- ops_vector.safe_push (commutate (e->ops[i]));
+ ops_vector.safe_push (commutate (e->ops[i], for_vec));
auto_vec< vec<operand *> > result;
auto_vec<operand *> v (e->ops.length ());
if (!e->is_commutative)
return ret;
+ /* The operation is always binary if it isn't inherently commutative. */
+ int natural_opno = commutative_op (e->operation);
+ unsigned int opno = natural_opno >= 0 ? natural_opno : 0;
for (unsigned i = 0; i < result.length (); ++i)
{
expr *ne = new expr (e);
+ if (operator_id *p = dyn_cast <operator_id *> (ne->operation))
+ {
+ if (comparison_code_p (p->code))
+ ne->operation = swap_tree_comparison (p);
+ }
+ else if (user_id *p = dyn_cast <user_id *> (ne->operation))
+ {
+ bool found_compare = false;
+ for (unsigned j = 0; j < p->substitutes.length (); ++j)
+ if (operator_id *q = dyn_cast <operator_id *> (p->substitutes[j]))
+ {
+ if (comparison_code_p (q->code)
+ && swap_tree_comparison (q) != q)
+ {
+ found_compare = true;
+ break;
+ }
+ }
+ if (found_compare)
+ {
+ user_id *newop = new user_id ("<internal>");
+ for (unsigned j = 0; j < p->substitutes.length (); ++j)
+ {
+ id_base *subst = p->substitutes[j];
+ if (operator_id *q = dyn_cast <operator_id *> (subst))
+ {
+ if (comparison_code_p (q->code))
+ subst = swap_tree_comparison (q);
+ }
+ newop->substitutes.safe_push (subst);
+ }
+ ne->operation = newop;
+ /* Search for 'p' inside the for vector and push 'newop'
+ to the same level. */
+ for (unsigned j = 0; newop && j < for_vec.length (); ++j)
+ for (unsigned k = 0; k < for_vec[j].length (); ++k)
+ if (for_vec[j][k] == p)
+ {
+ for_vec[j].safe_push (newop);
+ newop = NULL;
+ break;
+ }
+ }
+ }
ne->is_commutative = false;
- // result[i].length () is 2 since e->operation is binary
- for (unsigned j = result[i].length (); j; --j)
- ne->append_op (result[i][j-1]);
+ for (unsigned j = 0; j < result[i].length (); ++j)
+ {
+ int old_j = (j == opno ? opno + 1 : j == opno + 1 ? opno : j);
+ ne->append_op (result[i][old_j]);
+ }
ret.safe_push (ne);
}
static void
lower_commutative (simplify *s, vec<simplify *>& simplifiers)
{
- vec<operand *> matchers = commutate (s->match);
+ vec<operand *> matchers = commutate (s->match, s->for_vec);
for (unsigned i = 0; i < matchers.length (); ++i)
{
- simplify *ns = new simplify (s->kind, matchers[i], s->result,
+ simplify *ns = new simplify (s->kind, s->id, matchers[i], s->result,
s->for_vec, s->capture_ids);
simplifiers.safe_push (ns);
}
{
if (c->what)
return new capture (c->location, c->where,
- lower_opt_convert (c->what, oper, to_oper, strip));
+ lower_opt_convert (c->what, oper, to_oper, strip),
+ c->value_match);
else
return c;
}
vec<operand *> matchers = lower_opt_convert (s->match);
for (unsigned i = 0; i < matchers.length (); ++i)
{
- simplify *ns = new simplify (s->kind, matchers[i], s->result,
+ simplify *ns = new simplify (s->kind, s->id, matchers[i], s->result,
s->for_vec, s->capture_ids);
simplifiers.safe_push (ns);
}
lop = lower_cond (c->what);
for (unsigned i = 0; i < lop.length (); ++i)
- ro.safe_push (new capture (c->location, c->where, lop[i]));
+ ro.safe_push (new capture (c->location, c->where, lop[i],
+ c->value_match));
return ro;
}
}
for (unsigned j = 0; j < ocmp->ops.length (); ++j)
cmp->append_op (ocmp->ops[j]);
cmp->is_generic = true;
- ne->ops[0] = new capture (c->location, c->where, cmp);
+ ne->ops[0] = new capture (c->location, c->where, cmp,
+ c->value_match);
}
else
{
vec<operand *> matchers = lower_cond (s->match);
for (unsigned i = 0; i < matchers.length (); ++i)
{
- simplify *ns = new simplify (s->kind, matchers[i], s->result,
+ simplify *ns = new simplify (s->kind, s->id, matchers[i], s->result,
s->for_vec, s->capture_ids);
simplifiers.safe_push (ns);
}
}
+/* Return true if O refers to ID. */
+
+bool
+contains_id (operand *o, user_id *id)
+{
+ if (capture *c = dyn_cast<capture *> (o))
+ return c->what && contains_id (c->what, id);
+
+ if (expr *e = dyn_cast<expr *> (o))
+ {
+ if (e->operation == id)
+ return true;
+ for (unsigned i = 0; i < e->ops.length (); ++i)
+ if (contains_id (e->ops[i], id))
+ return true;
+ return false;
+ }
+
+ if (with_expr *w = dyn_cast <with_expr *> (o))
+ return (contains_id (w->with, id)
+ || contains_id (w->subexpr, id));
+
+ if (if_expr *ife = dyn_cast <if_expr *> (o))
+ return (contains_id (ife->cond, id)
+ || contains_id (ife->trueexpr, id)
+ || (ife->falseexpr && contains_id (ife->falseexpr, id)));
+
+ if (c_expr *ce = dyn_cast<c_expr *> (o))
+ return ce->capture_ids && ce->capture_ids->get (id->id);
+
+ return false;
+}
+
+
/* In AST operand O replace operator ID with operator WITH. */
operand *
if (!c->what)
return c;
return new capture (c->location, c->where,
- replace_id (c->what, id, with));
+ replace_id (c->what, id, with), c->value_match);
}
else if (expr *e = dyn_cast<expr *> (o))
{
{
operand *match_op = s->match;
operand *result_op = s->result;
- vec<std::pair<user_id *, id_base *> > subst;
- subst.create (n_ids);
+ auto_vec<std::pair<user_id *, id_base *> > subst (n_ids);
+ bool skip = false;
for (unsigned i = 0; i < n_ids; ++i)
{
user_id *id = ids[i];
id_base *oper = id->substitutes[j % id->substitutes.length ()];
+ if (oper == null_id
+ && (contains_id (match_op, id)
+ || contains_id (result_op, id)))
+ {
+ skip = true;
+ break;
+ }
subst.quick_push (std::make_pair (id, oper));
match_op = replace_id (match_op, id, oper);
if (result_op
&& !can_delay_subst)
result_op = replace_id (result_op, id, oper);
}
- simplify *ns = new simplify (s->kind, match_op, result_op,
+ if (skip)
+ continue;
+
+ simplify *ns = new simplify (s->kind, s->id, match_op, result_op,
vNULL, s->capture_ids);
ns->for_subst_vec.safe_splice (s->for_subst_vec);
if (result_op
&& can_delay_subst)
ns->for_subst_vec.safe_splice (subst);
- else
- subst.release ();
+
worklist.safe_push (ns);
}
}
unsigned cnt;
};
-struct sinfo_hashmap_traits : simple_hashmap_traits <pointer_hash <dt_simplify> >
+struct sinfo_hashmap_traits : simple_hashmap_traits<pointer_hash<dt_simplify>,
+ sinfo *>
{
static inline hashval_t hash (const key_type &);
static inline bool equal_keys (const key_type &, const key_type &);
typedef hash_map<void * /* unused */, sinfo *, sinfo_hashmap_traits>
sinfo_map_t;
+/* Current simplifier ID we are processing during insertion into the
+ decision tree. */
+static unsigned current_id;
-/* Decision tree base class, used for DT_TRUE and DT_NODE. */
+/* Decision tree base class, used for DT_NODE. */
struct dt_node
{
enum dt_type type;
unsigned level;
+ dt_node *parent;
vec<dt_node *> kids;
/* Statistics. */
unsigned total_size;
unsigned max_level;
- dt_node (enum dt_type type_): type (type_), level (0), kids (vNULL) {}
+ dt_node (enum dt_type type_, dt_node *parent_)
+ : type (type_), level (0), parent (parent_), kids (vNULL) {}
dt_node *append_node (dt_node *);
- dt_node *append_op (operand *, dt_node *parent = 0, unsigned pos = 0);
- dt_node *append_true_op (dt_node *parent = 0, unsigned pos = 0);
- dt_node *append_match_op (dt_operand *, dt_node *parent = 0, unsigned pos = 0);
+ dt_node *append_op (operand *, dt_node *parent, unsigned pos);
+ dt_node *append_true_op (operand *, dt_node *parent, unsigned pos);
+ dt_node *append_match_op (operand *, dt_operand *, dt_node *parent,
+ unsigned pos);
dt_node *append_simplify (simplify *, unsigned, dt_operand **);
virtual void gen (FILE *, int, bool) {}
void analyze (sinfo_map_t &);
};
-/* Generic decision tree node used for DT_OPERAND and DT_MATCH. */
+/* Generic decision tree node used for DT_OPERAND, DT_MATCH and DT_TRUE. */
struct dt_operand : public dt_node
{
operand *op;
dt_operand *match_dop;
- dt_operand *parent;
unsigned pos;
+ bool value_match;
+ unsigned for_id;
dt_operand (enum dt_type type, operand *op_, dt_operand *match_dop_,
- dt_operand *parent_ = 0, unsigned pos_ = 0)
- : dt_node (type), op (op_), match_dop (match_dop_),
- parent (parent_), pos (pos_) {}
+ dt_operand *parent_, unsigned pos_)
+ : dt_node (type, parent_), op (op_), match_dop (match_dop_),
+ pos (pos_), value_match (false), for_id (current_id) {}
void gen (FILE *, int, bool);
unsigned gen_predicate (FILE *, int, const char *, bool);
- unsigned gen_match_op (FILE *, int, const char *);
+ unsigned gen_match_op (FILE *, int, const char *, bool);
unsigned gen_gimple_expr (FILE *, int);
unsigned gen_generic_expr (FILE *, int, const char *);
sinfo *info;
dt_simplify (simplify *s_, unsigned pattern_no_, dt_operand **indexes_)
- : dt_node (DT_SIMPLIFY), s (s_), pattern_no (pattern_no_),
+ : dt_node (DT_SIMPLIFY, NULL), s (s_), pattern_no (pattern_no_),
indexes (indexes_), info (NULL) {}
void gen_1 (FILE *, int, bool, operand *);
is_a_helper <dt_operand *>::test (dt_node *n)
{
return (n->type == dt_node::DT_OPERAND
- || n->type == dt_node::DT_MATCH);
+ || n->type == dt_node::DT_MATCH
+ || n->type == dt_node::DT_TRUE);
}
template<>
void gen (FILE *f, bool gimple);
void print (FILE *f = stderr);
- decision_tree () { root = new dt_node (dt_node::DT_NODE); }
+ decision_tree () { root = new dt_node (dt_node::DT_NODE, NULL); }
static dt_node *insert_operand (dt_node *, operand *, dt_operand **indexes,
unsigned pos = 0, dt_node *parent = 0);
return cmp_operand ((as_a<dt_operand *> (n1))->op,
(as_a<dt_operand *> (n2))->op);
else if (n1->type == dt_node::DT_MATCH)
- return ((as_a<dt_operand *> (n1))->match_dop
- == (as_a<dt_operand *> (n2))->match_dop);
+ return (((as_a<dt_operand *> (n1))->match_dop
+ == (as_a<dt_operand *> (n2))->match_dop)
+ && ((as_a<dt_operand *> (n1))->value_match
+ == (as_a<dt_operand *> (n2))->value_match));
return false;
}
&& !ops.is_empty ()
&& ops.last ()->type == dt_node::DT_TRUE)
return ops.last ();
+ dt_operand *true_node = NULL;
for (int i = ops.length () - 1; i >= 0; --i)
{
/* But we can't merge across DT_TRUE nodes as they serve as
pattern order barriers to make sure that patterns apply
in order of appearance in case multiple matches are possible. */
if (ops[i]->type == dt_node::DT_TRUE)
- return NULL;
+ {
+ if (! true_node
+ || as_a <dt_operand *> (ops[i])->for_id > true_node->for_id)
+ true_node = as_a <dt_operand *> (ops[i]);
+ }
if (decision_tree::cmp_node (ops[i], p))
- return ops[i];
+ {
+ /* Unless we are processing the same pattern or the blocking
+ pattern is before the one we are going to merge with. */
+ if (true_node
+ && true_node->for_id != current_id
+ && true_node->for_id > as_a <dt_operand *> (ops[i])->for_id)
+ {
+ if (verbose >= 1)
+ {
+ location_t p_loc = 0;
+ if (p->type == dt_node::DT_OPERAND)
+ p_loc = as_a <dt_operand *> (p)->op->location;
+ location_t op_loc = 0;
+ if (ops[i]->type == dt_node::DT_OPERAND)
+ op_loc = as_a <dt_operand *> (ops[i])->op->location;
+ location_t true_loc = 0;
+ true_loc = true_node->op->location;
+ warning_at (p_loc,
+ "failed to merge decision tree node");
+ warning_at (op_loc,
+ "with the following");
+ warning_at (true_loc,
+ "because of the following which serves as ordering "
+ "barrier");
+ }
+ return NULL;
+ }
+ return ops[i];
+ }
}
return NULL;
}
/* Append a DT_TRUE decision tree node. */
dt_node *
-dt_node::append_true_op (dt_node *parent, unsigned pos)
+dt_node::append_true_op (operand *op, dt_node *parent, unsigned pos)
{
dt_operand *parent_ = safe_as_a<dt_operand *> (parent);
- dt_operand *n = new dt_operand (DT_TRUE, 0, 0, parent_, pos);
+ dt_operand *n = new dt_operand (DT_TRUE, op, 0, parent_, pos);
return append_node (n);
}
/* Append a DT_MATCH decision tree node. */
dt_node *
-dt_node::append_match_op (dt_operand *match_dop, dt_node *parent, unsigned pos)
+dt_node::append_match_op (operand *op, dt_operand *match_dop,
+ dt_node *parent, unsigned pos)
{
dt_operand *parent_ = as_a<dt_operand *> (parent);
- dt_operand *n = new dt_operand (DT_MATCH, 0, match_dop, parent_, pos);
+ dt_operand *n = new dt_operand (DT_MATCH, op, match_dop, parent_, pos);
return append_node (n);
}
dt_operand **indexes)
{
dt_simplify *n = new dt_simplify (s, pattern_no, indexes);
+ for (unsigned i = 0; i < kids.length (); ++i)
+ if (dt_simplify *s2 = dyn_cast <dt_simplify *> (kids[i]))
+ {
+ warning_at (s->match->location, "duplicate pattern");
+ warning_at (s2->s->match->location, "previous pattern defined here");
+ print_operand (s->match, stderr);
+ fprintf (stderr, "\n");
+ }
return append_node (n);
}
q = insert_operand (p, c->what, indexes, pos, parent);
else
{
- q = elm = p->append_true_op (parent, pos);
+ q = elm = p->append_true_op (o, parent, pos);
goto at_assert_elm;
}
// get to the last capture
unsigned cc_index = c->where;
dt_operand *match_op = indexes[cc_index];
- dt_operand temp (dt_node::DT_TRUE, 0, 0);
+ dt_operand temp (dt_node::DT_TRUE, 0, 0, 0, 0);
elm = decision_tree::find_node (p->kids, &temp);
if (elm == 0)
{
- dt_operand temp (dt_node::DT_MATCH, 0, match_op);
+ dt_operand temp (dt_node::DT_MATCH, 0, match_op, 0, 0);
+ temp.value_match = c->value_match;
elm = decision_tree::find_node (p->kids, &temp);
}
}
else
{
- dt_operand temp (dt_node::DT_OPERAND, c->what, 0);
+ dt_operand temp (dt_node::DT_OPERAND, c->what, 0, 0, 0);
elm = decision_tree::find_node (p->kids, &temp);
}
}
else
{
- p = p->append_match_op (indexes[capt_index], parent, pos);
+ p = p->append_match_op (o, indexes[capt_index], parent, pos);
+ as_a <dt_operand *>(p)->value_match = c->value_match;
if (c->what)
return insert_operand (p, c->what, indexes, 0, p);
else
void
decision_tree::insert (struct simplify *s, unsigned pattern_no)
{
+ current_id = s->id;
dt_operand **indexes = XCNEWVEC (dt_operand *, s->capture_max + 1);
dt_node *p = decision_tree::insert_operand (root, s->match, indexes);
p->append_simplify (s, pattern_no, indexes);
fprintf (f, "%p, ", (void *) s->indexes[i]);
fprintf (f, " } ");
}
+ if (is_a <dt_operand *> (p))
+ fprintf (f, " [%u]", as_a <dt_operand *> (p)->for_id);
}
- fprintf (stderr, " (%p), %u, %u\n", (void *) p, p->level, p->kids.length ());
+ fprintf (stderr, " (%p, %p), %u, %u\n",
+ (void *) p, (void *) p->parent, p->level, p->kids.length ());
for (unsigned i = 0; i < p->kids.length (); ++i)
decision_tree::print_node (p->kids[i], f, indent + 2);
bool force_single_use;
bool cond_expr_cond_p;
unsigned long toplevel_msk;
- int result_use_count;
+ unsigned match_use_count;
+ unsigned result_use_count;
unsigned same_as;
capture *c;
};
if (capture *c = dyn_cast <capture *> (o))
{
unsigned where = c->where;
+ info[where].match_use_count++;
info[where].toplevel_msk |= 1 << toplevel_arg;
info[where].force_no_side_effects_p |= conditional_p;
info[where].cond_expr_cond_p |= cond_expr_cond_p;
if (c->what
&& (e = dyn_cast <expr *> (c->what)))
{
- info[where].expr_p = true;
+ /* Zero-operand expression captures like ADDR_EXPR@0 are
+ similar as predicates -- if they are not mentioned in
+ the result we have to force them to have no side-effects. */
+ if (e->ops.length () != 0)
+ info[where].expr_p = true;
info[where].force_single_use |= e->force_single_use;
}
}
id = (const char *)n->val.str.text;
else
id = (const char *)CPP_HASHNODE (n->val.node.node)->ident.str;
- unsigned where = *e->capture_ids->get(id);
- info[info[where].same_as].force_no_side_effects_p = true;
+ unsigned *where = e->capture_ids->get(id);
+ if (! where)
+ fatal_at (n, "unknown capture id '%s'", id);
+ info[info[*where].same_as].force_no_side_effects_p = true;
if (verbose >= 1
&& !gimple)
warning_at (t, "capture escapes");
|| *op == VIEW_CONVERT_EXPR);
}
-/* Get the type to be used for generating operands of OP from the
+/* Get the type to be used for generating operand POS of OP from the
various sources. */
static const char *
-get_operand_type (id_base *op, const char *in_type,
+get_operand_type (id_base *op, unsigned pos,
+ const char *in_type,
const char *expr_type,
const char *other_oprnd_type)
{
else if (is_a <operator_id *> (op)
&& strcmp (as_a <operator_id *> (op)->tcc, "tcc_comparison") == 0)
return other_oprnd_type;
+ else if (*op == COND_EXPR
+ && pos == 0)
+ return "boolean_type_node";
+ else if (strncmp (op->id, "CFN_COND_", 9) == 0)
+ {
+ /* IFN_COND_* operands 1 and later by default have the same type
+ as the result. The type of operand 0 needs to be specified
+ explicitly. */
+ if (pos > 0 && expr_type)
+ return expr_type;
+ else if (pos > 0 && in_type)
+ return in_type;
+ else
+ return NULL;
+ }
else
{
/* Otherwise all types should match - choose one in order of
void
expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple,
int depth, const char *in_type, capture_info *cinfo,
- dt_operand **indexes, bool)
+ dt_operand **indexes, int)
{
id_base *opr = operation;
/* When we delay operator substituting during lowering of fors we
{
/* comparisons use boolean_type_node (or what gets in), but
their operands need to figure out the types themselves. */
- sprintf (optype, "boolean_type_node");
- type = optype;
+ if (in_type)
+ type = in_type;
+ else
+ {
+ sprintf (optype, "boolean_type_node");
+ type = optype;
+ }
+ in_type = NULL;
}
else if (*opr == COND_EXPR
- || *opr == VEC_COND_EXPR)
+ || *opr == VEC_COND_EXPR
+ || strncmp (opr->id, "CFN_COND_", 9) == 0)
{
/* Conditions are of the same type as their first alternative. */
sprintf (optype, "TREE_TYPE (ops%d[1])", depth);
char dest[32];
snprintf (dest, 32, "ops%d[%u]", depth, i);
const char *optype
- = get_operand_type (opr, in_type, expr_type,
+ = get_operand_type (opr, i, in_type, expr_type,
i == 0 ? NULL : op0type);
ops[i]->gen_transform (f, indent, dest, gimple, depth + 1, optype,
cinfo, indexes,
- ((!(*opr == COND_EXPR)
- && !(*opr == VEC_COND_EXPR))
- || i != 0));
+ (*opr == COND_EXPR
+ || *opr == VEC_COND_EXPR) && i == 0 ? 1 : 2);
}
const char *opr_name;
/* ??? Building a stmt can fail for various reasons here, seq being
NULL or the stmt referencing SSA names occuring in abnormal PHIs.
So if we fail here we should continue matching other patterns. */
- fprintf_indent (f, indent, "code_helper tem_code = %s;\n", opr_name);
- fprintf_indent (f, indent, "tree tem_ops[3] = { ");
+ fprintf_indent (f, indent, "gimple_match_op tem_op "
+ "(res_op->cond.any_else (), %s, %s", opr_name, type);
for (unsigned i = 0; i < ops.length (); ++i)
- fprintf (f, "ops%d[%u]%s", depth, i,
- i == ops.length () - 1 ? " };\n" : ", ");
+ fprintf (f, ", ops%d[%u]", depth, i);
+ fprintf (f, ");\n");
fprintf_indent (f, indent,
- "gimple_resimplify%d (lseq, &tem_code, %s, tem_ops, valueize);\n",
- ops.length (), type);
+ "gimple_resimplify%d (lseq, &tem_op, valueize);\n",
+ ops.length ());
fprintf_indent (f, indent,
- "res = maybe_push_res_to_seq (tem_code, %s, tem_ops, lseq);\n",
- type);
+ "res = maybe_push_res_to_seq (&tem_op, lseq);\n");
fprintf_indent (f, indent,
"if (!res) return false;\n");
if (*opr == CONVERT_EXPR)
else
{
fprintf_indent (f, indent, "{\n");
- fprintf_indent (f, indent, " tree decl = builtin_decl_implicit (%s);\n",
- opr_name);
- fprintf_indent (f, indent, " if (!decl) return NULL_TREE;\n");
- fprintf_indent (f, indent, " res = build_call_expr_loc (loc, "
- "decl, %d", ops.length());
+ fprintf_indent (f, indent, " res = maybe_build_call_expr_loc (loc, "
+ "%s, %s, %d", opr_name, type, ops.length());
}
for (unsigned i = 0; i < ops.length (); ++i)
fprintf (f, ", ops%d[%u]", depth, i);
fprintf (f, ");\n");
if (opr->kind != id_base::CODE)
- fprintf_indent (f, indent, "}\n");
+ {
+ fprintf_indent (f, indent, " if (!res)\n");
+ fprintf_indent (f, indent, " return NULL_TREE;\n");
+ fprintf_indent (f, indent, "}\n");
+ }
if (*opr == CONVERT_EXPR)
{
indent -= 2;
void
c_expr::gen_transform (FILE *f, int indent, const char *dest,
bool, int, const char *, capture_info *,
- dt_operand **, bool)
+ dt_operand **, int)
{
if (dest && nr_stmts == 1)
fprintf_indent (f, indent, "%s = ", dest);
void
capture::gen_transform (FILE *f, int indent, const char *dest, bool gimple,
int depth, const char *in_type, capture_info *cinfo,
- dt_operand **indexes, bool expand_compares)
+ dt_operand **indexes, int cond_handling)
{
if (what && is_a<expr *> (what))
{
}
}
- fprintf_indent (f, indent, "%s = captures[%u];\n", dest, where);
+ /* If in GENERIC some capture is used multiple times, unshare it except
+ when emitting the last use. */
+ if (!gimple
+ && cinfo->info.exists ()
+ && cinfo->info[cinfo->info[where].same_as].result_use_count > 1)
+ {
+ fprintf_indent (f, indent, "%s = unshare_expr (captures[%u]);\n",
+ dest, where);
+ cinfo->info[cinfo->info[where].same_as].result_use_count--;
+ }
+ else
+ fprintf_indent (f, indent, "%s = captures[%u];\n", dest, where);
/* ??? Stupid tcc_comparison GENERIC trees in COND_EXPRs. Deal
- with substituting a capture of that.
- ??? Returning false here will also not allow any other patterns
- to match. */
- if (gimple && expand_compares
+ with substituting a capture of that. */
+ if (gimple
+ && cond_handling != 0
&& cinfo->info[where].cond_expr_cond_p)
{
- fprintf_indent (f, indent, "if (COMPARISON_CLASS_P (%s))\n", dest);
- fprintf_indent (f, indent, " {\n");
- fprintf_indent (f, indent, " if (!seq) return false;\n");
- fprintf_indent (f, indent, " %s = gimple_build (seq, TREE_CODE (%s),"
- " TREE_TYPE (%s), TREE_OPERAND (%s, 0),"
- " TREE_OPERAND (%s, 1));\n",
- dest, dest, dest, dest, dest);
- fprintf_indent (f, indent, " }\n");
+ /* If substituting into a cond_expr condition, unshare. */
+ if (cond_handling == 1)
+ fprintf_indent (f, indent, "%s = unshare_expr (%s);\n", dest, dest);
+ /* If substituting elsewhere we might need to decompose it. */
+ else if (cond_handling == 2)
+ {
+ /* ??? Returning false here will also not allow any other patterns
+ to match unless this generator was split out. */
+ fprintf_indent (f, indent, "if (COMPARISON_CLASS_P (%s))\n", dest);
+ fprintf_indent (f, indent, " {\n");
+ fprintf_indent (f, indent, " if (!seq) return false;\n");
+ fprintf_indent (f, indent, " %s = gimple_build (seq,"
+ " TREE_CODE (%s),"
+ " TREE_TYPE (%s), TREE_OPERAND (%s, 0),"
+ " TREE_OPERAND (%s, 1));\n",
+ dest, dest, dest, dest, dest);
+ fprintf_indent (f, indent, " }\n");
+ }
}
}
char *
dt_operand::get_name (char *name)
{
- if (!parent)
+ if (! parent)
sprintf (name, "t");
else if (parent->level == 1)
sprintf (name, "op%u", pos);
else if (parent->type == dt_node::DT_MATCH)
- return parent->get_name (name);
+ return as_a <dt_operand *> (parent)->get_name (name);
else
sprintf (name, "o%u%u", parent->level, pos);
return name;
void
dt_operand::gen_opname (char *name, unsigned pos)
{
- if (!parent)
+ if (! parent)
sprintf (name, "op%u", pos);
else
sprintf (name, "o%u%u", level, pos);
a capture-match. */
unsigned
-dt_operand::gen_match_op (FILE *f, int indent, const char *opname)
+dt_operand::gen_match_op (FILE *f, int indent, const char *opname, bool)
{
char match_opname[20];
match_dop->get_name (match_opname);
- fprintf_indent (f, indent, "if (%s == %s || operand_equal_p (%s, %s, 0))\n",
- opname, match_opname, opname, match_opname);
+ if (value_match)
+ fprintf_indent (f, indent, "if ((%s == %s && ! TREE_SIDE_EFFECTS (%s)) "
+ "|| operand_equal_p (%s, %s, 0))\n",
+ opname, match_opname, opname, opname, match_opname);
+ else
+ fprintf_indent (f, indent, "if ((%s == %s && ! TREE_SIDE_EFFECTS (%s)) "
+ "|| (operand_equal_p (%s, %s, 0) "
+ "&& types_match (%s, %s)))\n",
+ opname, match_opname, opname, opname, match_opname,
+ opname, match_opname);
fprintf_indent (f, indent + 2, "{\n");
return 1;
}
expr *e = static_cast<expr *> (op);
id_base *id = e->operation;
unsigned n_ops = e->ops.length ();
+ unsigned n_braces = 0;
for (unsigned i = 0; i < n_ops; ++i)
{
/* ??? If this is a memory operation we can't (and should not)
match this. The only sensible operand types are
SSA names and invariants. */
- fprintf_indent (f, indent,
- "tree %s = TREE_OPERAND (gimple_assign_rhs1 (def), %i);\n",
- child_opname, i);
+ if (e->is_generic)
+ {
+ char opname[20];
+ get_name (opname);
+ fprintf_indent (f, indent,
+ "tree %s = TREE_OPERAND (%s, %i);\n",
+ child_opname, opname, i);
+ }
+ else
+ fprintf_indent (f, indent,
+ "tree %s = TREE_OPERAND "
+ "(gimple_assign_rhs1 (def), %i);\n",
+ child_opname, i);
fprintf_indent (f, indent,
"if ((TREE_CODE (%s) == SSA_NAME\n",
child_opname);
fprintf_indent (f, indent,
- " || is_gimple_min_invariant (%s))\n",
+ " || is_gimple_min_invariant (%s)))\n",
child_opname);
- fprintf_indent (f, indent,
- " && (%s = do_valueize (valueize, %s)))\n",
- child_opname, child_opname);
fprintf_indent (f, indent,
" {\n");
indent += 4;
+ n_braces++;
+ fprintf_indent (f, indent,
+ "%s = do_valueize (valueize, %s);\n",
+ child_opname, child_opname);
continue;
}
else
"tree %s = gimple_call_arg (def, %u);\n",
child_opname, i);
fprintf_indent (f, indent,
- "if ((%s = do_valueize (valueize, %s)))\n",
+ "%s = do_valueize (valueize, %s);\n",
child_opname, child_opname);
- fprintf_indent (f, indent, " {\n");
- indent += 4;
}
/* While the toplevel operands are canonicalized by the caller
after valueizing operands of sub-expressions we have to
re-canonicalize operand order. */
- if (operator_id *code = dyn_cast <operator_id *> (id))
+ int opno = commutative_op (id);
+ if (opno >= 0)
{
- /* ??? We can't canonicalize tcc_comparison operands here
- because that requires changing the comparison code which
- we already matched... */
- if (commutative_tree_code (code->code)
- || commutative_ternary_tree_code (code->code))
- {
- char child_opname0[20], child_opname1[20];
- gen_opname (child_opname0, 0);
- gen_opname (child_opname1, 1);
- fprintf_indent (f, indent,
- "if (tree_swap_operands_p (%s, %s, false))\n",
- child_opname0, child_opname1);
- fprintf_indent (f, indent,
- " std::swap (%s, %s);\n",
- child_opname0, child_opname1);
- }
+ char child_opname0[20], child_opname1[20];
+ gen_opname (child_opname0, opno);
+ gen_opname (child_opname1, opno + 1);
+ fprintf_indent (f, indent,
+ "if (tree_swap_operands_p (%s, %s))\n",
+ child_opname0, child_opname1);
+ fprintf_indent (f, indent,
+ " std::swap (%s, %s);\n",
+ child_opname0, child_opname1);
}
- return n_ops;
+ return n_braces;
}
/* Generate GENERIC matching code for the decision tree operand. */
preds.safe_push (op);
else
{
- if (gimple)
+ if (gimple && !e->is_generic)
gimple_exprs.safe_push (op);
else
generic_exprs.safe_push (op);
fprintf_indent (f, indent,
"case SSA_NAME:\n");
fprintf_indent (f, indent,
- " if (do_valueize (valueize, %s) != NULL_TREE)\n",
+ " if (gimple *def_stmt = get_def (valueize, %s))\n",
kid_opname);
fprintf_indent (f, indent,
" {\n");
- fprintf_indent (f, indent,
- " gimple *def_stmt = SSA_NAME_DEF_STMT (%s);\n",
- kid_opname);
-
indent += 6;
if (exprs_len)
{
if (fns_len)
{
fprintf_indent (f, indent,
- "%sif (gimple_call_builtin_p (def_stmt, BUILT_IN_NORMAL))\n",
+ "%sif (gcall *def = dyn_cast <gcall *>"
+ " (def_stmt))\n",
exprs_len ? "else " : "");
fprintf_indent (f, indent,
- " {\n");
- fprintf_indent (f, indent,
- " gcall *def = as_a <gcall *> (def_stmt);\n");
- fprintf_indent (f, indent,
- " tree fndecl = gimple_call_fndecl (def);\n");
- fprintf_indent (f, indent,
- " switch (DECL_FUNCTION_CODE (fndecl))\n");
- fprintf_indent (f, indent,
- " {\n");
+ " switch (gimple_call_combined_fn (def))\n");
- indent += 6;
+ indent += 4;
+ fprintf_indent (f, indent, "{\n");
for (unsigned i = 0; i < fns_len; ++i)
{
expr *e = as_a <expr *>(fns[i]->op);
fprintf_indent (f, indent, "default:;\n");
fprintf_indent (f, indent, "}\n");
- indent -= 6;
- fprintf_indent (f, indent, " }\n");
+ indent -= 4;
}
indent -= 6;
fprintf_indent (f, indent, " }\n");
+ /* See if there is SSA_NAME among generic_exprs and if yes, emit it
+ here rather than in the next loop. */
+ for (unsigned i = 0; i < generic_exprs.length (); ++i)
+ {
+ expr *e = as_a <expr *>(generic_exprs[i]->op);
+ id_base *op = e->operation;
+ if (*op == SSA_NAME && (exprs_len || fns_len))
+ {
+ fprintf_indent (f, indent + 4, "{\n");
+ generic_exprs[i]->gen (f, indent + 6, gimple);
+ fprintf_indent (f, indent + 4, "}\n");
+ }
+ }
+
fprintf_indent (f, indent, " break;\n");
}
id_base *op = e->operation;
if (*op == CONVERT_EXPR || *op == NOP_EXPR)
fprintf_indent (f, indent, "CASE_CONVERT:\n");
+ else if (*op == SSA_NAME && (exprs_len || fns_len))
+ /* Already handled above. */
+ continue;
else
fprintf_indent (f, indent, "case %s:\n", op->id);
fprintf_indent (f, indent, " {\n");
fprintf_indent (f, indent,
"case CALL_EXPR:\n");
fprintf_indent (f, indent,
- " {\n");
- fprintf_indent (f, indent,
- " tree fndecl = get_callee_fndecl (%s);\n",
+ " switch (get_call_combined_fn (%s))\n",
kid_opname);
fprintf_indent (f, indent,
- " if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)\n");
- fprintf_indent (f, indent,
- " switch (DECL_FUNCTION_CODE (fndecl))\n");
- fprintf_indent (f, indent,
- " {\n");
- indent += 8;
+ " {\n");
+ indent += 4;
for (unsigned j = 0; j < generic_fns.length (); ++j)
{
fprintf_indent (f, indent, " break;\n");
fprintf_indent (f, indent, " }\n");
}
+ fprintf_indent (f, indent, "default:;\n");
- indent -= 8;
- fprintf_indent (f, indent, " default:;\n");
- fprintf_indent (f, indent, " }\n");
- fprintf_indent (f, indent, " break;\n");
- fprintf_indent (f, indent, " }\n");
+ indent -= 4;
+ fprintf_indent (f, indent, " }\n");
+ fprintf_indent (f, indent, " break;\n");
}
/* Close switch (TREE_CODE ()). */
expr *e = as_a <expr *> (preds[i]->op);
predicate_id *p = as_a <predicate_id *> (e->operation);
preds[i]->get_name (kid_opname);
+ fprintf_indent (f, indent, "{\n");
+ indent += 2;
fprintf_indent (f, indent, "tree %s_pops[%d];\n", kid_opname, p->nargs);
fprintf_indent (f, indent, "if (%s_%s (%s, %s_pops%s))\n",
gimple ? "gimple" : "tree",
}
preds[i]->gen_kids (f, indent + 4, gimple);
fprintf (f, "}\n");
+ indent -= 2;
+ fprintf_indent (f, indent, "}\n");
}
for (unsigned i = 0; i < others.length (); ++i)
else if (type == DT_TRUE)
;
else if (type == DT_MATCH)
- n_braces = gen_match_op (f, indent, opname);
+ n_braces = gen_match_op (f, indent, opname, gimple);
else
gcc_unreachable ();
}
}
- fprintf_indent (f, indent, "if (dump_file && (dump_flags & TDF_DETAILS)) "
- "fprintf (dump_file, \"Applying pattern ");
+ fprintf_indent (f, indent, "if (__builtin_expect (dump_file && (dump_flags & TDF_FOLDING), 0)) "
+ "fprintf (dump_file, \"%s ",
+ s->kind == simplify::SIMPLIFY
+ ? "Applying pattern" : "Matching expression");
+ fprintf (f, "%%s:%%d, %%s:%%d\\n\", ");
output_line_directive (f,
- result ? result->location : s->match->location, true);
- fprintf (f, ", %%s:%%d\\n\", __FILE__, __LINE__);\n");
+ result ? result->location : s->match->location, true,
+ true);
+ fprintf (f, ", __FILE__, __LINE__);\n");
if (!result)
{
else if (is_a <predicate_id *> (opr))
is_predicate = true;
if (!is_predicate)
- fprintf_indent (f, indent, "*res_code = %s;\n",
+ fprintf_indent (f, indent, "res_op->set_op (%s, type, %d);\n",
*e->operation == CONVERT_EXPR
- ? "NOP_EXPR" : e->operation->id);
+ ? "NOP_EXPR" : e->operation->id,
+ e->ops.length ());
for (unsigned j = 0; j < e->ops.length (); ++j)
{
char dest[32];
- snprintf (dest, 32, "res_ops[%d]", j);
+ if (is_predicate)
+ snprintf (dest, 32, "res_ops[%d]", j);
+ else
+ snprintf (dest, 32, "res_op->ops[%d]", j);
const char *optype
- = get_operand_type (opr,
+ = get_operand_type (opr, j,
"type", e->expr_type,
- j == 0 ? NULL : "TREE_TYPE (res_ops[0])");
+ j == 0 ? NULL
+ : "TREE_TYPE (res_op->ops[0])");
/* We need to expand GENERIC conditions we captured from
- COND_EXPRs. */
- bool expand_generic_cond_exprs_p
- = (!is_predicate
- /* But avoid doing that if the GENERIC condition is
- valid - which it is in the first operand of COND_EXPRs
- and VEC_COND_EXRPs. */
- && ((!(*opr == COND_EXPR)
- && !(*opr == VEC_COND_EXPR))
- || j != 0));
+ COND_EXPRs and we need to unshare them when substituting
+ into COND_EXPRs. */
+ int cond_handling = 0;
+ if (!is_predicate)
+ cond_handling = ((*opr == COND_EXPR
+ || *opr == VEC_COND_EXPR) && j == 0) ? 1 : 2;
e->ops[j]->gen_transform (f, indent, dest, true, 1, optype,
- &cinfo,
- indexes, expand_generic_cond_exprs_p);
+ &cinfo, indexes, cond_handling);
}
/* Re-fold the toplevel result. It's basically an embedded
gimple_build w/o actually building the stmt. */
if (!is_predicate)
fprintf_indent (f, indent,
- "gimple_resimplify%d (lseq, res_code, type, "
- "res_ops, valueize);\n", e->ops.length ());
+ "gimple_resimplify%d (lseq, res_op,"
+ " valueize);\n", e->ops.length ());
}
else if (result->type == operand::OP_CAPTURE
|| result->type == operand::OP_C_EXPR)
{
- result->gen_transform (f, indent, "res_ops[0]", true, 1, "type",
- &cinfo, indexes, false);
- fprintf_indent (f, indent, "*res_code = TREE_CODE (res_ops[0]);\n");
+ fprintf_indent (f, indent, "tree tem;\n");
+ result->gen_transform (f, indent, "tem", true, 1, "type",
+ &cinfo, indexes);
+ fprintf_indent (f, indent, "res_op->set_value (tem);\n");
if (is_a <capture *> (result)
&& cinfo.info[as_a <capture *> (result)->where].cond_expr_cond_p)
{
/* ??? Stupid tcc_comparison GENERIC trees in COND_EXPRs. Deal
with substituting a capture of that. */
fprintf_indent (f, indent,
- "if (COMPARISON_CLASS_P (res_ops[0]))\n");
+ "if (COMPARISON_CLASS_P (tem))\n");
fprintf_indent (f, indent,
" {\n");
fprintf_indent (f, indent,
- " tree tem = res_ops[0];\n");
+ " res_op->ops[0] = TREE_OPERAND (tem, 0);\n");
fprintf_indent (f, indent,
- " res_ops[0] = TREE_OPERAND (tem, 0);\n");
- fprintf_indent (f, indent,
- " res_ops[1] = TREE_OPERAND (tem, 1);\n");
+ " res_op->ops[1] = TREE_OPERAND (tem, 1);\n");
fprintf_indent (f, indent,
" }\n");
}
else if (is_a <predicate_id *> (opr))
is_predicate = true;
/* Search for captures used multiple times in the result expression
- and dependent on TREE_SIDE_EFFECTS emit a SAVE_EXPR. */
+ and wrap them in a SAVE_EXPR. Allow as many uses as in the
+ original expression. */
if (!is_predicate)
for (int i = 0; i < s->capture_max + 1; ++i)
{
- if (cinfo.info[i].same_as != (unsigned)i)
+ if (cinfo.info[i].same_as != (unsigned)i
+ || cinfo.info[i].cse_p)
continue;
- if (!cinfo.info[i].force_no_side_effects_p
- && cinfo.info[i].result_use_count > 1)
- {
- fprintf_indent (f, indent,
- "if (TREE_SIDE_EFFECTS (captures[%d]))\n",
- i);
- fprintf_indent (f, indent,
- " captures[%d] = save_expr (captures[%d]);\n",
- i, i);
- }
+ if (cinfo.info[i].result_use_count
+ > cinfo.info[i].match_use_count)
+ fprintf_indent (f, indent,
+ "if (! tree_invariant_p (captures[%d])) "
+ "return NULL_TREE;\n", i);
}
for (unsigned j = 0; j < e->ops.length (); ++j)
{
snprintf (dest, 32, "res_op%d", j);
}
const char *optype
- = get_operand_type (opr,
+ = get_operand_type (opr, j,
"type", e->expr_type,
j == 0
? NULL : "TREE_TYPE (res_op0)");
*e->operation == CONVERT_EXPR
? "NOP_EXPR" : e->operation->id);
else
- {
- fprintf_indent (f, indent,
- "{\n");
- fprintf_indent (f, indent,
- " tree decl = builtin_decl_implicit (%s);\n",
- e->operation->id);
- fprintf_indent (f, indent,
- " if (!decl) return NULL_TREE;\n");
- fprintf_indent (f, indent,
- " res = build_call_expr_loc "
- "(loc, decl, %d",
- e->ops.length());
- }
+ fprintf_indent (f, indent,
+ "res = maybe_build_call_expr_loc (loc, "
+ "%s, type, %d", e->operation->id,
+ e->ops.length());
for (unsigned j = 0; j < e->ops.length (); ++j)
fprintf (f, ", res_op%d", j);
fprintf (f, ");\n");
if (!is_a <operator_id *> (opr))
- fprintf_indent (f, indent, "}\n");
+ {
+ fprintf_indent (f, indent, "if (!res)\n");
+ fprintf_indent (f, indent, " return NULL_TREE;\n");
+ }
}
}
}
s->capture_max + 1, indexes[0]->get_name (opname));
for (int i = 1; i <= s->capture_max; ++i)
- fprintf (f, ", %s", indexes[i]->get_name (opname));
+ {
+ if (!indexes[i])
+ break;
+ fprintf (f, ", %s", indexes[i]->get_name (opname));
+ }
fprintf (f, " };\n");
}
{
if (gimple)
{
- fprintf_indent (f, indent, "if (%s (res_code, res_ops, seq, "
+ fprintf_indent (f, indent, "if (%s (res_op, seq, "
"valueize, type, captures", info->fname);
for (unsigned i = 0; i < s->for_subst_vec.length (); ++i)
- fprintf (f, ", %s", s->for_subst_vec[i].second->id);
+ if (s->for_subst_vec[i].first->used)
+ fprintf (f, ", %s", s->for_subst_vec[i].second->id);
fprintf (f, "))\n");
fprintf_indent (f, indent, " return true;\n");
}
fprintf (f, ", op%d", i);
fprintf (f, ", captures");
for (unsigned i = 0; i < s->for_subst_vec.length (); ++i)
- fprintf (f, ", %s", s->for_subst_vec[i].second->id);
+ {
+ if (s->for_subst_vec[i].first->used)
+ fprintf (f, ", %s", s->for_subst_vec[i].second->id);
+ }
fprintf (f, ");\n");
fprintf_indent (f, indent, "if (res) return res;\n");
}
{
for (unsigned i = 0; i < s->for_subst_vec.length (); ++i)
{
+ if (! s->for_subst_vec[i].first->used)
+ continue;
if (is_a <operator_id *> (s->for_subst_vec[i].second))
- fprintf_indent (f, indent, "enum tree_code %s = %s;\n",
+ fprintf_indent (f, indent, "const enum tree_code %s = %s;\n",
s->for_subst_vec[i].first->id,
s->for_subst_vec[i].second->id);
else if (is_a <fn_id *> (s->for_subst_vec[i].second))
- fprintf_indent (f, indent, "enum built_in_function %s = %s;\n",
+ fprintf_indent (f, indent, "const combined_fn %s = %s;\n",
s->for_subst_vec[i].first->id,
s->for_subst_vec[i].second->id);
else
fcnt++);
if (gimple)
fprintf (f, "\nstatic bool\n"
- "%s (code_helper *res_code, tree *res_ops,\n"
- " gimple_seq *seq, tree (*valueize)(tree) "
- "ATTRIBUTE_UNUSED,\n"
- " tree ARG_UNUSED (type), tree *ARG_UNUSED "
+ "%s (gimple_match_op *res_op, gimple_seq *seq,\n"
+ " tree (*valueize)(tree) ATTRIBUTE_UNUSED,\n"
+ " const tree ARG_UNUSED (type), tree *ARG_UNUSED "
"(captures)\n",
s->fname);
else
{
fprintf (f, "\nstatic tree\n"
- "%s (location_t ARG_UNUSED (loc), tree ARG_UNUSED (type),\n",
+ "%s (location_t ARG_UNUSED (loc), const tree ARG_UNUSED (type),\n",
(*iter).second->fname);
for (unsigned i = 0;
i < as_a <expr *>(s->s->s->match)->ops.length (); ++i)
}
for (unsigned i = 0; i < s->s->s->for_subst_vec.length (); ++i)
{
+ if (! s->s->s->for_subst_vec[i].first->used)
+ continue;
if (is_a <operator_id *> (s->s->s->for_subst_vec[i].second))
- fprintf (f, ", enum tree_code ARG_UNUSED (%s)",
+ fprintf (f, ", const enum tree_code ARG_UNUSED (%s)",
s->s->s->for_subst_vec[i].first->id);
else if (is_a <fn_id *> (s->s->s->for_subst_vec[i].second))
- fprintf (f, ", enum built_in_function ARG_UNUSED (%s)",
+ fprintf (f, ", const combined_fn ARG_UNUSED (%s)",
s->s->s->for_subst_vec[i].first->id);
}
}
fprintf (stderr, "removed %u duplicate tails\n", rcnt);
- for (unsigned n = 1; n <= 3; ++n)
+ for (unsigned n = 1; n <= 5; ++n)
{
/* First generate split-out functions. */
for (unsigned i = 0; i < root->kids.length (); i++)
if (gimple)
fprintf (f, "\nstatic bool\n"
- "gimple_simplify_%s (code_helper *res_code, tree *res_ops,\n"
- " gimple_seq *seq, tree (*valueize)(tree) "
+ "gimple_simplify_%s (gimple_match_op *res_op,"
+ " gimple_seq *seq,\n"
+ " tree (*valueize)(tree) "
"ATTRIBUTE_UNUSED,\n"
" code_helper ARG_UNUSED (code), tree "
"ARG_UNUSED (type)\n",
else
fprintf (f, "\nstatic tree\n"
"generic_simplify_%s (location_t ARG_UNUSED (loc), enum "
- "tree_code ARG_UNUSED (code), tree ARG_UNUSED (type)",
+ "tree_code ARG_UNUSED (code), const tree ARG_UNUSED (type)",
e->operation->id);
for (unsigned i = 0; i < n; ++i)
fprintf (f, ", tree op%d", i);
tail-calls to the split-out functions. */
if (gimple)
fprintf (f, "\nstatic bool\n"
- "gimple_simplify (code_helper *res_code, tree *res_ops,\n"
- " gimple_seq *seq, tree (*valueize)(tree),\n"
- " code_helper code, tree type");
+ "gimple_simplify (gimple_match_op *res_op, gimple_seq *seq,\n"
+ " tree (*valueize)(tree) ATTRIBUTE_UNUSED,\n"
+ " code_helper code, const tree type");
else
fprintf (f, "\ntree\n"
"generic_simplify (location_t loc, enum tree_code code, "
- "tree type ATTRIBUTE_UNUSED");
+ "const tree type ATTRIBUTE_UNUSED");
for (unsigned i = 0; i < n; ++i)
fprintf (f, ", tree op%d", i);
fprintf (f, ")\n");
is_a <fn_id *> (e->operation) ? "-" : "",
e->operation->id);
if (gimple)
- fprintf (f, " return gimple_simplify_%s (res_code, res_ops, "
+ fprintf (f, " return gimple_simplify_%s (res_op, "
"seq, valueize, code, type", e->operation->id);
else
fprintf (f, " return generic_simplify_%s (loc, code, type",
"%s%s (tree t%s%s)\n"
"{\n", gimple ? "gimple_" : "tree_", p->id,
p->nargs > 0 ? ", tree *res_ops" : "",
- gimple ? ", tree (*valueize)(tree)" : "");
+ gimple ? ", tree (*valueize)(tree) ATTRIBUTE_UNUSED" : "");
/* Conveniently make 'type' available. */
- fprintf_indent (f, 2, "tree type = TREE_TYPE (t);\n");
+ fprintf_indent (f, 2, "const tree type = TREE_TYPE (t);\n");
if (!gimple)
fprintf_indent (f, 2, "if (TREE_SIDE_EFFECTS (t)) return false;\n");
const cpp_token *eat_ident (const char *);
const char *get_number ();
+ unsigned get_internal_capture_id ();
+
id_base *parse_operation ();
operand *parse_capture (operand *, bool);
operand *parse_expr ();
c_expr *parse_c_expr (cpp_ttype);
operand *parse_op ();
- void record_operlist (source_location, user_id *);
+ void record_operlist (location_t, user_id *);
void parse_pattern ();
operand *parse_result (operand *, predicate_id *);
vec<simplify *>&, operand *, operand *);
void parse_simplify (simplify::simplify_kind,
vec<simplify *>&, predicate_id *, operand *);
- void parse_for (source_location);
- void parse_if (source_location);
- void parse_predicates (source_location);
- void parse_operator_list (source_location);
+ void parse_for (location_t);
+ void parse_if (location_t);
+ void parse_predicates (location_t);
+ void parse_operator_list (location_t);
+
+ void finish_match_operand (operand *);
cpp_reader *r;
vec<c_expr *> active_ifs;
vec<user_id *> oper_lists;
cid_map_t *capture_ids;
+ unsigned last_id;
public:
vec<simplify *> simplifiers;
{
token = cpp_get_token (r);
}
- while (token->type == CPP_PADDING
- && token->type != CPP_EOF);
+ while (token->type == CPP_PADDING);
return token;
}
{
token = cpp_peek_token (r, i++);
}
- while ((token->type == CPP_PADDING
- && token->type != CPP_EOF)
+ while (token->type == CPP_PADDING
|| (--num > 0));
/* If we peek at EOF this is a fatal error as it leaves the
cpp_reader in unusable state. Assume we really wanted a
return (const char *)token->val.str.text;
}
+/* Return a capture ID that can be used internally. */
+
+unsigned
+parser::get_internal_capture_id ()
+{
+ unsigned newid = capture_ids->elements ();
+ /* Big enough for a 32-bit UINT_MAX plus prefix. */
+ char id[13];
+ bool existed;
+ sprintf (id, "__%u", newid);
+ capture_ids->get_or_insert (xstrdup (id), &existed);
+ if (existed)
+ fatal ("reserved capture id '%s' already used", id);
+ return newid;
+}
/* Record an operator-list use for transparent for handling. */
void
-parser::record_operlist (source_location loc, user_id *p)
+parser::record_operlist (location_t loc, user_id *p)
{
if (!oper_lists_set->add (p))
{
if (active_fors.length() == 0)
record_operlist (id_tok->src_loc, p);
else
- fatal_at (id_tok, "operator-list %s cannot be exapnded inside 'for'", id);
+ fatal_at (id_tok, "operator-list %s cannot be expanded inside 'for'", id);
}
return op;
}
struct operand *
parser::parse_capture (operand *op, bool require_existing)
{
- source_location src_loc = eat_token (CPP_ATSIGN)->src_loc;
+ location_t src_loc = eat_token (CPP_ATSIGN)->src_loc;
const cpp_token *token = peek ();
const char *id = NULL;
+ bool value_match = false;
+ /* For matches parse @@ as a value-match denoting the prevailing operand. */
+ if (token->type == CPP_ATSIGN
+ && ! (token->flags & PREV_WHITE)
+ && parsing_match_operand)
+ {
+ eat_token (CPP_ATSIGN);
+ token = peek ();
+ value_match = true;
+ }
if (token->type == CPP_NUMBER)
id = get_number ();
else if (token->type == CPP_NAME)
fatal_at (src_loc, "unknown capture id");
num = next_id;
}
- return new capture (src_loc, num, op);
+ return new capture (src_loc, num, op, value_match);
}
/* Parse an expression
while (*sp)
{
if (*sp == 'c')
+ {
+ if (operator_id *p
+ = dyn_cast<operator_id *> (e->operation))
+ {
+ if (!commutative_tree_code (p->code)
+ && !comparison_code_p (p->code))
+ fatal_at (token, "operation is not commutative");
+ }
+ else if (user_id *p = dyn_cast<user_id *> (e->operation))
+ for (unsigned i = 0;
+ i < p->substitutes.length (); ++i)
+ {
+ if (operator_id *q
+ = dyn_cast<operator_id *> (p->substitutes[i]))
+ {
+ if (!commutative_tree_code (q->code)
+ && !comparison_code_p (q->code))
+ fatal_at (token, "operation %s is not "
+ "commutative", q->id);
+ }
+ }
+ is_commutative = true;
+ }
+ else if (*sp == 'C')
is_commutative = true;
else if (*sp == 's')
{
if (token->type == CPP_ATSIGN
&& !(token->flags & PREV_WHITE))
- op = parse_capture (e, !parsing_match_operand);
+ op = parse_capture (e, false);
else if (force_capture)
{
- unsigned num = capture_ids->elements ();
- char id[8];
- bool existed;
- sprintf (id, "__%u", num);
- capture_ids->get_or_insert (xstrdup (id), &existed);
- if (existed)
- fatal_at (token, "reserved capture id '%s' already used", id);
- op = new capture (token->src_loc, num, e);
+ unsigned num = get_internal_capture_id ();
+ op = new capture (token->src_loc, num, e, false);
}
else
op = e;
e->operation->id, e->operation->nargs, e->ops.length ());
if (is_commutative)
{
- if (e->ops.length () == 2)
+ if (e->ops.length () == 2
+ || commutative_op (e->operation) >= 0)
e->is_commutative = true;
else
- fatal_at (token, "only binary operators or function with "
- "two arguments can be marked commutative");
+ fatal_at (token, "only binary operators or functions with "
+ "two arguments can be marked commutative, "
+ "unless the operation is known to be inherently "
+ "commutative");
}
e->expr_type = expr_type;
return op;
unsigned opencnt;
vec<cpp_token> code = vNULL;
unsigned nr_stmts = 0;
- source_location loc = eat_token (start)->src_loc;
+ location_t loc = eat_token (start)->src_loc;
if (start == CPP_OPEN_PAREN)
end = CPP_CLOSE_PAREN;
else if (start == CPP_OPEN_BRACE)
else if (token->type == end
&& --opencnt == 0)
break;
+ else if (token->type == CPP_EOF)
+ fatal_at (token, "unexpected end of file");
/* This is a lame way of counting the number of statements. */
if (token->type == CPP_SEMICOLON)
active_fors.safe_push (oper_lists);
simplifiers.safe_push
- (new simplify (kind, match, result,
+ (new simplify (kind, last_id++, match, result,
active_fors.copy (), capture_ids));
if (!oper_lists.is_empty ())
else if (peek_ident ("switch"))
{
token = eat_ident ("switch");
- source_location ifloc = eat_token (CPP_OPEN_PAREN)->src_loc;
+ location_t ifloc = eat_token (CPP_OPEN_PAREN)->src_loc;
eat_ident ("if");
if_expr *ife = new if_expr (ifloc);
operand *res = ife;
const cpp_token *loc = peek ();
parsing_match_operand = true;
struct operand *match = parse_op ();
+ finish_match_operand (match);
parsing_match_operand = false;
if (match->type == operand::OP_CAPTURE && !matcher)
fatal_at (loc, "outermost expression cannot be captured");
subst = <ident> '(' <ident>... ')' */
void
-parser::parse_for (source_location)
+parser::parse_for (location_t)
{
auto_vec<const cpp_token *> user_id_tokens;
vec<user_id *> user_ids = vNULL;
/* Insert the user defined operators into the operator hash. */
const char *id = get_ident ();
- if (get_operator (id) != NULL)
+ if (get_operator (id, true) != NULL)
fatal_at (token, "operator already defined");
user_id *op = new user_id (id);
id_base **slot = operators->find_slot_with_hash (op, op->hashval, INSERT);
while ((token = peek_ident ()) != 0)
{
const char *oper = get_ident ();
- id_base *idb = get_operator (oper);
+ id_base *idb = get_operator (oper, true);
if (idb == NULL)
fatal_at (token, "no such operator '%s'", oper);
if (*idb == CONVERT0 || *idb == CONVERT1 || *idb == CONVERT2
oprs = '(' 'define_operator_list' <ident> <ident>... ')' */
void
-parser::parse_operator_list (source_location)
+parser::parse_operator_list (location_t)
{
const cpp_token *token = peek ();
const char *id = get_ident ();
- if (get_operator (id) != 0)
+ if (get_operator (id, true) != 0)
fatal_at (token, "operator %s already defined", id);
user_id *op = new user_id (id, true);
{
token = peek ();
const char *oper = get_ident ();
- id_base *idb = get_operator (oper);
+ id_base *idb = get_operator (oper, true);
if (idb == 0)
fatal_at (token, "no such operator '%s'", oper);
if = '(' 'if' '(' <c-expr> ')' <pattern> ')' */
void
-parser::parse_if (source_location)
+parser::parse_if (location_t)
{
c_expr *ifexpr = parse_c_expr (CPP_OPEN_PAREN);
preds = '(' 'define_predicates' <ident>... ')' */
void
-parser::parse_predicates (source_location)
+parser::parse_predicates (location_t)
{
do
{
else if (strcmp (id, "match") == 0)
{
bool with_args = false;
- source_location e_loc = peek ()->src_loc;
+ location_t e_loc = peek ()->src_loc;
if (peek ()->type == CPP_OPEN_PAREN)
{
eat_token (CPP_OPEN_PAREN);
eat_token (CPP_CLOSE_PAREN);
}
+/* Helper for finish_match_operand, collecting captures of OP in CPTS
+ recursively. */
+
+static void
+walk_captures (operand *op, vec<vec<capture *> > cpts)
+{
+ if (! op)
+ return;
+
+ if (capture *c = dyn_cast <capture *> (op))
+ {
+ cpts[c->where].safe_push (c);
+ walk_captures (c->what, cpts);
+ }
+ else if (expr *e = dyn_cast <expr *> (op))
+ for (unsigned i = 0; i < e->ops.length (); ++i)
+ walk_captures (e->ops[i], cpts);
+}
+
+/* Finish up OP which is a match operand. */
+
+void
+parser::finish_match_operand (operand *op)
+{
+ /* Look for matching captures, diagnose mis-uses of @@ and apply
+ early lowering and distribution of value_match. */
+ auto_vec<vec<capture *> > cpts;
+ cpts.safe_grow_cleared (capture_ids->elements ());
+ walk_captures (op, cpts);
+ for (unsigned i = 0; i < cpts.length (); ++i)
+ {
+ capture *value_match = NULL;
+ for (unsigned j = 0; j < cpts[i].length (); ++j)
+ {
+ if (cpts[i][j]->value_match)
+ {
+ if (value_match)
+ fatal_at (cpts[i][j]->location, "duplicate @@");
+ value_match = cpts[i][j];
+ }
+ }
+ if (cpts[i].length () == 1 && value_match)
+ fatal_at (value_match->location, "@@ without a matching capture");
+ if (value_match)
+ {
+ /* Duplicate prevailing capture with the existing ID, create
+ a fake ID and rewrite all captures to use it. This turns
+ @@1 into @__<newid>@1 and @1 into @__<newid>. */
+ value_match->what = new capture (value_match->location,
+ value_match->where,
+ value_match->what, false);
+ /* Create a fake ID and rewrite all captures to use it. */
+ unsigned newid = get_internal_capture_id ();
+ for (unsigned j = 0; j < cpts[i].length (); ++j)
+ {
+ cpts[i][j]->where = newid;
+ cpts[i][j]->value_match = true;
+ }
+ }
+ cpts[i].release ();
+ }
+}
+
/* Main entry of the parser. Repeatedly parse outer control structures. */
parser::parser (cpp_reader *r_)
capture_ids = NULL;
user_predicates = vNULL;
parsing_match_operand = false;
+ last_id = 0;
const cpp_token *token = next ();
while (token->type != CPP_EOF)
r = cpp_create_reader (CLK_GNUC99, NULL, line_table);
cpp_callbacks *cb = cpp_get_callbacks (r);
- cb->error = error_cb;
+ cb->diagnostic = diagnostic_cb;
+
+ /* Add the build directory to the #include "" search path. */
+ cpp_dir *dir = XCNEW (cpp_dir);
+ dir->name = getpwd ();
+ if (!dir->name)
+ dir->name = ASTRDUP (".");
+ cpp_set_include_chains (r, dir, NULL, false);
if (!cpp_read_main_file (r, input))
return 1;
cpp_define (r, gimple ? "GIMPLE=1": "GENERIC=1");
cpp_define (r, gimple ? "GENERIC=0": "GIMPLE=0");
+ null_id = new id_base (id_base::NULL_ID, "null");
+
/* Pre-seed operators. */
operators = new hash_table<id_base> (1024);
#define DEFTREECODE(SYM, STRING, TYPE, NARGS) \
add_operator (SYM, # SYM, # TYPE, NARGS);
#define END_OF_BASE_TREE_CODES
#include "tree.def"
-add_operator (CONVERT0, "CONVERT0", "tcc_unary", 1);
-add_operator (CONVERT1, "CONVERT1", "tcc_unary", 1);
-add_operator (CONVERT2, "CONVERT2", "tcc_unary", 1);
-add_operator (VIEW_CONVERT0, "VIEW_CONVERT0", "tcc_unary", 1);
-add_operator (VIEW_CONVERT1, "VIEW_CONVERT1", "tcc_unary", 1);
-add_operator (VIEW_CONVERT2, "VIEW_CONVERT2", "tcc_unary", 1);
+add_operator (CONVERT0, "convert0", "tcc_unary", 1);
+add_operator (CONVERT1, "convert1", "tcc_unary", 1);
+add_operator (CONVERT2, "convert2", "tcc_unary", 1);
+add_operator (VIEW_CONVERT0, "view_convert0", "tcc_unary", 1);
+add_operator (VIEW_CONVERT1, "view_convert1", "tcc_unary", 1);
+add_operator (VIEW_CONVERT2, "view_convert2", "tcc_unary", 1);
#undef END_OF_BASE_TREE_CODES
#undef DEFTREECODE
??? Cannot use N (name) as that is targetm.emultls.get_address
for BUILT_IN_EMUTLS_GET_ADDRESS ... */
#define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) \
- add_builtin (ENUM, # ENUM);
+ add_function (ENUM, "CFN_" # ENUM);
#include "builtins.def"
-#undef DEF_BUILTIN
+
+#define DEF_INTERNAL_FN(CODE, NAME, FNSPEC) \
+ add_function (IFN_##CODE, "CFN_" #CODE);
+#include "internal-fn.def"
/* Parse ahead! */
parser p (r);