From: Robin Dapp <[email protected]>
This patch adds foundational support for register filters that depend on
or reference another operand. The patch borrows heavily from Richard's
work on register filters.
As opposed to register filters, dependent filters need a runtime
evaluation function that dispatches to a target function (usually).
Common code can then evaluate the filter via
eval_dependent_filter (id, ...);
gcc/ChangeLog:
* doc/md.texi: Document dependent filters.
* doc/tm.texi: Ditto.
* doc/tm.texi.in: Ditto.
* genconfig.cc (main): Add number of dependent filters.
* genpreds.cc (add_constraint): Add dependent-filter reference
operand.
(process_define_register_constraint): Handle dependent filter.
(write_get_register_filter): Ditto.
(write_get_register_filter_id): Ditto.
(write_dependent_filter_helpers_h): Ditto.
(write_dependent_filter_functions_c): Ditto.
(write_tm_preds_h): Ditto.
(write_insn_preds_c): Ditto.
(main): Ditto.
* gensupport.cc (process_define_register_constraint): Ditto.
* gensupport.h (get_register_filter_id): Declare.
* rtl.def (DEFINE_REGISTER_CONSTRAINT): Adjust for dependent
filter.
---
gcc/doc/md.texi | 19 +++++-
gcc/doc/tm.texi | 3 +
gcc/doc/tm.texi.in | 3 +
gcc/genconfig.cc | 1 +
gcc/genpreds.cc | 165 +++++++++++++++++++++++++++++++++++++++++++--
gcc/gensupport.cc | 37 ++++++----
gcc/gensupport.h | 2 +
gcc/rtl.def | 12 +++-
8 files changed, 222 insertions(+), 20 deletions(-)
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 94d258c3c91..2e1a1ef19f0 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -4376,7 +4376,7 @@ Register constraints correspond directly to register
classes.
@xref{Register Classes}. There is thus not much flexibility in their
definitions.
-@deffn {MD Expression} define_register_constraint name regclass docstring
[filter]
+@deffn {MD Expression} define_register_constraint name regclass docstring
[filter] [opno]
All arguments are string constants.
@var{name} is the name of the constraint, as it will appear in
@code{match_operand} expressions. If @var{name} is a multi-letter
@@ -4426,6 +4426,23 @@ sometimes necessary for a register of class @var{c} to
be aligned
to @var{n}, the first register in @var{c} should itself by divisible
by @var{n}.
+If even more fine-grained control is needed, the optional @var{opno}
+can be specified. It indicates that the constraint is dependent on
+another operand indicated by @var{opno}. When it is specified, a
+@var{filter} must be specified as well. The filter function will then
+be called with the current regno and mode of the operand the constraint
+refers to as well as ref_regno and ref_mode of the operand @var{opno}
+the filter depends on additionally.
+
+As such dependent filters are dynamic and cannot be precomputed, it is
+advisable to use them very sparingly. Register allocation is a very
+compile-time sensitive part of GCC, and too many, or too complicated
+dynamic filters may increase the compile time significantly.
+
+@smallexample
+(define_register_constraint "Wtt" "V_REGS" "..." "riscv_widen_overlap_ok
(regno, mode, ref_regno, ref_mode)" "0")
+@end smallexample
+
@var{docstring} is a sentence documenting the meaning of the
constraint. Docstrings are explained further below.
@end deffn
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index a4ae17decb0..8daf219cadc 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2501,6 +2501,9 @@ Therefore, register classes cannot be used to enforce a
requirement for
a register pair to start with an even-numbered register. The way to
specify this requirement is with @code{TARGET_HARD_REGNO_MODE_OK},
or with a filter expression in a @code{define_register_constraint}.
+An operand's constraint that is dependent on another operand's register
+or mode can be expressed using a dependent register constraint with
+an additional operand index in @code{define_register_constraint}.
Register classes used for input-operands of bitwise-and or shift
instructions have a special requirement: each such class must have, for
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 1acda0c264c..3bd63db221e 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2077,6 +2077,9 @@ Therefore, register classes cannot be used to enforce a
requirement for
a register pair to start with an even-numbered register. The way to
specify this requirement is with @code{TARGET_HARD_REGNO_MODE_OK},
or with a filter expression in a @code{define_register_constraint}.
+An operand's constraint that is dependent on another operand's register
+or mode can be expressed using a dependent register constraint with
+an additional operand index in @code{define_register_constraint}.
Register classes used for input-operands of bitwise-and or shift
instructions have a special requirement: each such class must have, for
diff --git a/gcc/genconfig.cc b/gcc/genconfig.cc
index d5b27593e5e..ef81f2ffc58 100644
--- a/gcc/genconfig.cc
+++ b/gcc/genconfig.cc
@@ -361,6 +361,7 @@ main (int argc, const char **argv)
}
printf ("#define NUM_REGISTER_FILTERS %d\n", register_filters.length ());
+ printf ("#define NUM_DEPENDENT_FILTERS %u\n", num_dependent_filters);
puts ("\n#endif /* GCC_INSN_CONFIG_H */");
diff --git a/gcc/genpreds.cc b/gcc/genpreds.cc
index b98fbab08ef..1b0913a2187 100644
--- a/gcc/genpreds.cc
+++ b/gcc/genpreds.cc
@@ -678,6 +678,9 @@ public:
const char *regclass; /* for register constraints */
rtx exp; /* for other constraints */
const char *filter; /* the register filter condition, or null if none */
+ /* The operand a dependent filter depends on (references), or -1 if this
+ constraint has no dependent filter. */
+ int dependent_filter_ref_opno;
unsigned int is_register : 1;
unsigned int is_const_int : 1;
unsigned int is_const_dbl : 1;
@@ -690,6 +693,10 @@ public:
unsigned int maybe_allows_mem : 1;
};
+/* List of dependent filters. A dependent filter ID is an index of this
+ list. */
+static vec<class constraint_data *> dependent_filters;
+
/* Overview of all constraints beginning with a given letter. */
static class constraint_data *
@@ -779,7 +786,8 @@ static void
add_constraint (const char *name, const char *regclass,
rtx exp, bool is_memory, bool is_special_memory,
bool is_relaxed_memory, bool is_address, file_location loc,
- const char *filter = nullptr)
+ const char *filter = nullptr,
+ int ref_opno = -1)
{
class constraint_data *c, **iter, **slot;
const char *p;
@@ -913,6 +921,9 @@ add_constraint (const char *name, const char *regclass,
c->regclass = regclass;
c->exp = exp;
c->filter = filter;
+ c->dependent_filter_ref_opno = ref_opno;
+ if (ref_opno >= 0)
+ dependent_filters.safe_push (c);
c->is_register = regclass != 0;
c->is_const_int = is_const_int;
c->is_const_dbl = is_const_dbl;
@@ -969,9 +980,37 @@ process_define_constraint (md_rtx_info *info)
static void
process_define_register_constraint (md_rtx_info *info)
{
+ /* Get the optional reference operand number the filter is
+ dependent on. */
+ const char *ref_str = XSTR (info->def, 4);
+ int ref_opno = -1;
+
+ if (ref_str && !XSTR (info->def, 3))
+ {
+ error_at (info->loc, "depends_on operand specified without"
+ " a filter expression");
+ return;
+ }
+
+ /* Parse it. */
+ if (ref_str)
+ {
+ char *end;
+ long n = strtol (ref_str, &end, 10);
+ /* MAX_RECOG_OPERANDS = 30 is defined in insn-config.h
+ but is not available here. */
+ if (*ref_str == '\0' || *end != '\0' || n < 0 || n >= 30)
+ {
+ error_at (info->loc, "depends_on operand index '%s' must be a"
+ " non-negative integer less than 30", ref_str);
+ return;
+ }
+ ref_opno = (int) n;
+ }
+
add_constraint (XSTR (info->def, 0), XSTR (info->def, 1),
0, false, false, false, false, info->loc,
- XSTR (info->def, 3));
+ XSTR (info->def, 3), ref_opno);
}
/* Put the constraints into enum order. We want to keep constraints
@@ -1456,7 +1495,7 @@ write_get_register_filter ()
register_filters.is_empty () ? "" : " c");
printf ("{\n");
FOR_ALL_CONSTRAINTS (c)
- if (c->is_register && c->filter)
+ if (c->is_register && c->filter && c->dependent_filter_ref_opno < 0)
{
printf (" if (c == CONSTRAINT_%s)\n", c->c_name);
printf (" return &this_target_constraints->register_filters[%d];\n",
@@ -1481,7 +1520,7 @@ write_get_register_filter_id ()
register_filters.is_empty () ? "" : " c");
printf ("{\n");
FOR_ALL_CONSTRAINTS (c)
- if (c->is_register && c->filter)
+ if (c->is_register && c->filter && c->dependent_filter_ref_opno < 0)
{
printf (" if (c == CONSTRAINT_%s)\n", c->c_name);
printf (" return %d;\n", get_register_filter_id (c->filter));
@@ -1490,6 +1529,119 @@ write_get_register_filter_id ()
"}\n");
}
+/* Print the get_dependent_filter_id, get_dependent_filter_ref functions,
+ and a forward declaration for the eval_dependent_filter function.
+ The first function maps a constraint to a dependent filter id or
+ returns -1.
+ The second function returns the referenced opno for a given filter
+ id, and the third function evaluates a dynamic filter
+ (again indexed by id) to true or false for a given regno, mode,
+ referenced regno and referenced mode. */
+
+static void
+write_dependent_filter_helpers_h ()
+{
+ bool empty_p = dependent_filters.is_empty ();
+
+ printf ("\n"
+ "static inline int\n"
+ "get_dependent_filter_id (constraint_num%s)\n"
+ "{\n",
+ empty_p ? "" : " c");
+ if (empty_p)
+ printf (" return -1;\n");
+ else
+ {
+ for (unsigned i = 0; i < dependent_filters.length (); ++i)
+ printf (" if (c == CONSTRAINT_%s) return %u;\n",
+ dependent_filters[i]->c_name, i);
+ printf (" return -1;\n");
+ }
+ printf ("}\n");
+
+ printf ("\n"
+ "static inline int\n"
+ "get_dependent_filter_ref (int id)\n"
+ "{\n");
+ if (empty_p)
+ printf (" (void) id;\n return -1;\n");
+ else
+ {
+ printf (" switch (id)\n {\n");
+ for (unsigned i = 0; i < dependent_filters.length (); ++i)
+ printf (" case %u: return %d;\n",
+ i, dependent_filters[i]->dependent_filter_ref_opno);
+ printf (" default: return -1;\n }\n");
+ }
+ printf ("}\n");
+
+ printf ("\n"
+ "extern bool\n"
+ "eval_dependent_filter (int id, unsigned int regno,\n"
+ " machine_mode mode,\n"
+ " unsigned int ref_regno,\n"
+ " machine_mode ref_mode);\n");
+}
+
+/* Print the eval_dependent_filter dispatcher function, and
+ eval_dependent_filter_0, 1,... functions that the dispatcher calls.
+ An eval_dependent_filter_0 calls a target-specified function
+ that determines whether a given regno, mode, referenced regno, and
+ referenced mode combination is valid for this particular constraint,
+ returning true or false. */
+
+static void
+write_dependent_filter_functions_c ()
+{
+ if (dependent_filters.is_empty ())
+ {
+ printf ("\n"
+ "bool\n"
+ "eval_dependent_filter (int, unsigned int, machine_mode,\n"
+ " unsigned int, machine_mode)\n"
+ "{\n"
+ " return true;\n"
+ "}\n");
+ return;
+ }
+
+ for (unsigned id = 0; id < dependent_filters.length (); ++id)
+ {
+ const char *filter = dependent_filters[id]->filter;
+ printf ("\n"
+ "/* Dependent filter id=%u. */\n"
+ "static inline bool\n"
+ "dependent_filter_%u (unsigned int regno, machine_mode mode,\n"
+ " unsigned int ref_regno,\n"
+ " machine_mode ref_mode)\n"
+ "{\n"
+ " (void) regno; (void) mode;\n"
+ " (void) ref_regno; (void) ref_mode;\n"
+ " return ",
+ id, id);
+ rtx_reader_ptr->print_c_condition (stdout, filter);
+ printf (";\n}\n");
+ }
+
+ printf ("\n"
+ "bool\n"
+ "eval_dependent_filter (int id, unsigned int regno,\n"
+ " machine_mode mode, unsigned int ref_regno,\n"
+ " machine_mode ref_mode)\n"
+ "{\n"
+ " switch (id)\n"
+ " {\n");
+ for (unsigned id = 0; id < dependent_filters.length (); ++id)
+ printf (" case %u:\n"
+ " return dependent_filter_%u (regno, mode,\n"
+ " ref_regno, ref_mode);\n",
+ id, id);
+ printf (" default:\n"
+ " return true;\n"
+ " }\n"
+ "}\n");
+}
+
/* Write tm-preds.h. Unfortunately, it is impossible to forward-declare
an enumeration in portable C, so we have to condition all these
prototypes on HAVE_MACHINE_MODES. */
@@ -1679,6 +1831,7 @@ write_tm_preds_h (void)
write_get_register_filter ();
write_get_register_filter_id ();
+ write_dependent_filter_helpers_h ();
puts ("#endif /* tm-preds.h */");
}
@@ -1749,6 +1902,8 @@ write_insn_preds_c (void)
write_insn_const_int_ok_for_constraint ();
write_init_reg_class_start_regs ();
+
+ write_dependent_filter_functions_c ();
}
/* Argument parsing. */
@@ -1807,6 +1962,8 @@ main (int argc, const char **argv)
break;
}
+ gcc_assert (dependent_filters.length () == num_dependent_filters);
+
choose_enum_order ();
if (gen_header)
diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
index 35b78044390..1da33310620 100644
--- a/gcc/gensupport.cc
+++ b/gcc/gensupport.cc
@@ -407,6 +407,8 @@ static hash_map<nofree_string_hash, unsigned int>
register_filter_map;
/* All register filter conditions, indexed by identifier. */
vec<const char *> register_filters;
+unsigned int num_dependent_filters;
+
/* Return the unique identifier for filter condition FILTER. Identifiers
are assigned automatically when the define_register_constraint is
parsed. */
@@ -424,19 +426,30 @@ get_register_filter_id (const char *filter)
static void
process_define_register_constraint (rtx desc, file_location loc)
{
- /* Assign identifiers to each unique register filter condition. */
- if (const char *filter = XSTR (desc, 3))
+ const char *filter = XSTR (desc, 3);
+ if (!filter)
+ return;
+
+ const char *depends_on = XSTR (desc, 4);
+
+ if (depends_on)
{
- bool existed = false;
- unsigned int &id = register_filter_map.get_or_insert (filter, &existed);
- if (!existed)
- {
- id = register_filters.length ();
- if (id == 32)
- fatal_at (loc, "too many distinct register filters, maximum"
- " is 32");
- register_filters.safe_push (filter);
- }
+ /* Nothing to do here, genpreds, handles dependent filter building.
+ genconfig needs to know the number of dependent filters, so at
+ least count them. */
+ num_dependent_filters++;
+ return;
+ }
+
+ bool existed = false;
+ unsigned int &id = register_filter_map.get_or_insert (filter, &existed);
+ if (!existed)
+ {
+ id = register_filters.length ();
+ if (id == 32)
+ fatal_at (loc, "too many distinct register filters, maximum"
+ " is 32");
+ register_filters.safe_push (filter);
}
}
diff --git a/gcc/gensupport.h b/gcc/gensupport.h
index b71256a95c6..86dd1103436 100644
--- a/gcc/gensupport.h
+++ b/gcc/gensupport.h
@@ -111,6 +111,8 @@ extern unsigned int num_optabs;
extern vec<const char *> register_filters;
extern unsigned int get_register_filter_id (const char *);
+extern unsigned int num_dependent_filters;
+
/* Information about an instruction name that matches an optab pattern. */
struct optab_pattern
{
diff --git a/gcc/rtl.def b/gcc/rtl.def
index ad98fd59172..6abc103e457 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -1017,9 +1017,15 @@ DEF_RTL_EXPR(DEFINE_SPECIAL_PREDICATE,
"define_special_predicate", "ses", RTX_EX
2: A docstring for this constraint, in Texinfo syntax; not currently
used, in future will be incorporated into the manual's list of
machine-specific operand constraints.
- 3: A C expression that evalutes to true if "regno" is a valid
- start register. */
-DEF_RTL_EXPR(DEFINE_REGISTER_CONSTRAINT, "define_register_constraint", "sssS",
RTX_EXTRA)
+ 3: A C expression that evaluates to true if "regno" is a valid
+ start register. When the constraint has a dependent filter
+ (see operand 4), the expression can also reference "ref_regno"
+ and "ref_mode" of the operand it depends on.
+ 4: Optional operand index for a dependent, dynamic filter.
+ When specified, the C expression of operand 3 is evaluated at
+ runtime with the additional "ref_regno" and "ref_mode" of the
+ referenced operand. */
+DEF_RTL_EXPR(DEFINE_REGISTER_CONSTRAINT, "define_register_constraint",
"sssSS", RTX_EXTRA)
/* Definition of a non-register operand constraint. These look at the
operand and decide whether it fits the constraint.
--
2.53.0