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 | 45 +++++++++++-
gcc/doc/tm.texi | 3 +
gcc/doc/tm.texi.in | 3 +
gcc/genconfig.cc | 6 +-
gcc/genpreds.cc | 166 +++++++++++++++++++++++++++++++++++++++++++--
gcc/gensupport.cc | 37 ++++++----
gcc/gensupport.h | 2 +
gcc/rtl.def | 10 ++-
8 files changed, 251 insertions(+), 21 deletions(-)
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 90755bdf389..7213d5088e3 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -4384,7 +4384,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
@@ -4434,6 +4434,49 @@ 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 (a ``referenced'' 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.
+
+As an example, the riscv port must ensure that for widening vector
+instructions, the destination register group either does not overlap
+or only overlaps specific parts of the source. A dynamic register
+constraint would then be:
+
+@smallexample
+(define_register_constraint "Wtt" "V_REGS" "..." "riscv_widen_overlap_ok
(regno, mode, ref_regno, ref_mode)" "0")
+@end smallexample
+
+and the accompanying target-code function @var{riscv_widen_overlap_ok} might
+do:
+@smallexample
+unsigned int wide_nregs = riscv_hard_regno_nregs (wide_regno, wide_mode);
+unsigned int nregs = riscv_hard_regno_nregs (regno, mode);
+
+/* Overlap is only allowed in the highest-numbered part of the wider
+ destination. */
+if (regno == wide_regno)
+ return false;
+
+if (regno >= wide_regno + (wide_nregs - nregs))
+ return true;
+
+/* No overlap is OK. */
+if (regno < wide_regno)
+ return true;
+
+return false;
+@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 ba037b88053..e726745a8e1 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2490,6 +2490,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 0944563c9cb..3868a68e9f3 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -2075,6 +2075,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..d24337c4604 100644
--- a/gcc/genconfig.cc
+++ b/gcc/genconfig.cc
@@ -276,7 +276,10 @@ main (int argc, const char **argv)
puts ("#ifndef GCC_INSN_CONFIG_H");
puts ("#define GCC_INSN_CONFIG_H\n");
- /* Allow at least 30 operands for the sake of asm constructs. */
+ /* Allow at least 30 operands for the sake of asm constructs.
+ This magic number is also used in genpreds to determine
+ validity of a referenced operand when parsing dynamic register
+ filters. */
/* ??? We *really* ought to reorganize things such that there
is no fixed upper bound. */
max_recog_operands = 29; /* We will add 1 later. */
@@ -361,6 +364,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 61d39063f37..e775994205c 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,7 @@ 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, int ref_opno)
{
class constraint_data *c, **iter, **slot;
const char *p;
@@ -913,6 +920,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;
@@ -962,16 +972,44 @@ process_define_constraint (md_rtx_info *info)
GET_CODE (info->def) == DEFINE_SPECIAL_MEMORY_CONSTRAINT,
GET_CODE (info->def) == DEFINE_RELAXED_MEMORY_CONSTRAINT,
GET_CODE (info->def) == DEFINE_ADDRESS_CONSTRAINT,
- info->loc);
+ info->loc, nullptr, -1);
}
/* Process a DEFINE_REGISTER_CONSTRAINT expression, C. */
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, "reference 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, "reference 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 +1494,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 +1519,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 +1528,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 +1830,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 +1901,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 +1961,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 cebb976273d..5fcc30d340e 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 *reference_op = XSTR (desc, 4);
+
+ if (reference_op)
{
- 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 e83d6554c80..ae757929de2 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -1018,8 +1018,14 @@ DEF_RTL_EXPR(DEFINE_SPECIAL_PREDICATE,
"define_special_predicate", "ses", RTX_EX
used, in future will be incorporated into the manual's list of
machine-specific operand constraints.
3: A C expression that evaluates to true if "regno" is a valid
- start register. */
-DEF_RTL_EXPR(DEFINE_REGISTER_CONSTRAINT, "define_register_constraint", "sssS",
RTX_EXTRA)
+ 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.54.0