@@ -4513,8 +4513,8 @@ 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
-All three arguments are string constants.
+@deffn {MD Expression} define_register_constraint name regclass docstring [filter]
+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
constraint its length shall be the same for all constraints starting
@@ -4526,6 +4526,43 @@ look at the operand. The usual use of expressions is to map some
register constraints to @code{NO_REGS} when the register class
is not available on a given subarchitecture.
+If an operand occupies multiple hard registers, the constraint
+requires all of those registers to belong to @var{regclass}.
+For example, if @var{regclass} is @code{GENERAL_REGS} and
+@code{GENERAL_REGS} contains registers @code{r0} to @code{r15},
+the constraint does not allow @var{r15} to be used for modes
+that occupy more than one register.
+
+@cindex @code{TARGET_HARD_REGNO_MODE_OK} and constraints
+The choice of register is also constrained by @code{TARGET_HARD_REGNO_MODE_OK}.
+For example, if @code{TARGET_HARD_REGNO_MODE_OK} disallows @samp{(reg:DI r1)},
+that requirement applies to all constraints whose classes include @code{r1}.
+
+However, it is sometimes useful to impose extra operand-specific
+requirements on the register number. For example, a target might not
+want to prevent @emph{all} odd-even pairs from holding @code{DImode}
+values, but it might still need to prevent specific operands from
+having an odd-numbered register. The optional @var{filter} argument
+exists for such cases. When given, @var{filter} is a C++ expression
+that evaluates to true if @code{regno} is a valid register for the
+operand. If an operand occupies multiple registers, the condition
+applies only to the first register.
+
+For example:
+
+@smallexample
+(define_register_constraint "e" "GENERAL_REGS" "..." "regno % 2 == 0")
+@end smallexample
+
+defines a constraint that requires an even-numbered general register.
+
+Filter conditions that impose an alignment are encouraged to test
+the alignment of @code{regno} itself, as in the example, rather than
+calculate an offset relative to the start of the class. If it is
+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}.
+
@var{docstring} is a sentence documenting the meaning of the
constraint. Docstrings are explained further below.
@end deffn
@@ -2478,7 +2478,8 @@ When a value occupying several consecutive registers is expected in a
certain class, all the registers used must belong to that class.
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}.
+specify this requirement is with @code{TARGET_HARD_REGNO_MODE_OK},
+or with a filter expression in a @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
@@ -2060,7 +2060,8 @@ When a value occupying several consecutive registers is expected in a
certain class, all the registers used must belong to that class.
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}.
+specify this requirement is with @code{TARGET_HARD_REGNO_MODE_OK},
+or with a filter expression in a @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
@@ -360,6 +360,8 @@ main (int argc, const char **argv)
printf ("#define MAX_INSNS_PER_PEEP2 0\n");
}
+ printf ("#define NUM_REGISTER_FILTERS %d\n", register_filters.length ());
+
puts ("\n#endif /* GCC_INSN_CONFIG_H */");
if (ferror (stdout) || fflush (stdout) || fclose (stdout))
@@ -677,6 +677,7 @@ public:
size_t namelen;
const char *regclass; /* for register constraints */
rtx exp; /* for other constraints */
+ const char *filter; /* the register filter condition, or null if none */
unsigned int is_register : 1;
unsigned int is_const_int : 1;
unsigned int is_const_dbl : 1;
@@ -763,7 +764,8 @@ mangle (const char *name)
is the register class, if any; EXP is the expression to test, if any;
IS_MEMORY, IS_SPECIAL_MEMORY, IS_RELAXED_MEMORY and IS_ADDRESS indicate
memory, special memory, and address constraints, respectively; LOC is the .md
- file location.
+ file location; FILTER is the filter condition for a register constraint,
+ or null if none.
Not all combinations of arguments are valid; most importantly, REGCLASS is
mutually exclusive with EXP, and
@@ -776,7 +778,8 @@ mangle (const char *name)
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)
+ bool is_relaxed_memory, bool is_address, file_location loc,
+ const char *filter = nullptr)
{
class constraint_data *c, **iter, **slot;
const char *p;
@@ -908,6 +911,7 @@ add_constraint (const char *name, const char *regclass,
c->namelen = namelen;
c->regclass = regclass;
c->exp = exp;
+ c->filter = filter;
c->is_register = regclass != 0;
c->is_const_int = is_const_int;
c->is_const_dbl = is_const_dbl;
@@ -966,7 +970,8 @@ static void
process_define_register_constraint (md_rtx_info *info)
{
add_constraint (XSTR (info->def, 0), XSTR (info->def, 1),
- 0, false, false, false, false, info->loc);
+ 0, false, false, false, false, info->loc,
+ XSTR (info->def, 3));
}
/* Put the constraints into enum order. We want to keep constraints
@@ -1319,6 +1324,34 @@ write_insn_const_int_ok_for_constraint (void)
" return false;\n"
"}\n");
}
+
+/* Print the init_reg_class_start_regs function, which initializes
+ this_target_constraints->register_filters from the C conditions
+ in the define_register_constraints. */
+static void
+write_init_reg_class_start_regs ()
+{
+ printf ("\n"
+ "void\n"
+ "init_reg_class_start_regs ()\n"
+ "{\n");
+ if (!register_filters.is_empty ())
+ {
+ printf (" for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER;"
+ " ++regno)\n"
+ " {\n");
+ for (unsigned int i = 0; i < register_filters.length (); ++i)
+ {
+ printf (" if (");
+ rtx_reader_ptr->print_c_condition (register_filters[i]);
+ printf (")\n"
+ " SET_HARD_REG_BIT (%s[%d], regno);\n",
+ "this_target_constraints->register_filters", i);
+ }
+ printf (" }\n");
+ }
+ printf ("}\n");
+}
/* Write a definition for a function NAME that returns true if a given
constraint_num is in the range [START, END). */
@@ -1401,6 +1434,54 @@ print_type_tree (const vec <std::pair <unsigned int, const char *> > &vec,
printf ("%*sreturn %s;\n", indent, "", fallback);
}
+/* Print the get_register_filter function, which returns a pointer
+ to the start register filter for a given constraint, or null if none. */
+static void
+write_get_register_filter ()
+{
+ constraint_data *c;
+
+ printf ("\n"
+ "#ifdef GCC_HARD_REG_SET_H\n"
+ "static inline const HARD_REG_SET *\n"
+ "get_register_filter (constraint_num%s)\n",
+ register_filters.is_empty () ? "" : " c");
+ printf ("{\n");
+ FOR_ALL_CONSTRAINTS (c)
+ if (c->is_register && c->filter)
+ {
+ printf (" if (c == CONSTRAINT_%s)\n", c->c_name);
+ printf (" return &this_target_constraints->register_filters[%d];\n",
+ get_register_filter_id (c->filter));
+ }
+ printf (" return nullptr;\n"
+ "}\n"
+ "#endif\n");
+}
+
+/* Print the get_register_filter_id function, which returns the index
+ of the given constraint's register filter in
+ this_target_constraints->register_filters, or -1 if none. */
+static void
+write_get_register_filter_id ()
+{
+ constraint_data *c;
+
+ printf ("\n"
+ "static inline int\n"
+ "get_register_filter_id (constraint_num%s)\n",
+ register_filters.is_empty () ? "" : " c");
+ printf ("{\n");
+ FOR_ALL_CONSTRAINTS (c)
+ if (c->is_register && c->filter)
+ {
+ printf (" if (c == CONSTRAINT_%s)\n", c->c_name);
+ printf (" return %d;\n", get_register_filter_id (c->filter));
+ }
+ printf (" return -1;\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. */
@@ -1425,6 +1506,53 @@ write_tm_preds_h (void)
puts ("#endif /* HAVE_MACHINE_MODES */\n");
+ /* Print the definition of the target_constraints structure. */
+ printf ("#ifdef GCC_HARD_REG_SET_H\n"
+ "struct target_constraints {\n"
+ " HARD_REG_SET register_filters[%d];\n",
+ MAX (register_filters.length (), 1));
+ printf ("};\n"
+ "\n"
+ "extern struct target_constraints default_target_constraints;\n"
+ "#if SWITCHABLE_TARGET\n"
+ "extern struct target_constraints *this_target_constraints;\n"
+ "#else\n"
+ "#define this_target_constraints (&default_target_constraints)\n"
+ "#endif\n");
+
+ /* Print TEST_REGISTER_FILTER_BIT, which tests whether register REGNO
+ is a valid start register for register filter ID. */
+ printf ("\n"
+ "#define TEST_REGISTER_FILTER_BIT(ID, REGNO) \\\n");
+ if (register_filters.is_empty ())
+ printf (" ((void) (ID), (void) (REGNO), false)\n");
+ else
+ printf (" TEST_HARD_REG_BIT ("
+ "this_target_constraints->register_filters[ID], REGNO)\n");
+
+ /* Print test_register_filters, which tests whether register REGNO
+ is a valid start register for the mask of register filters in MASK. */
+ printf ("\n"
+ "inline bool\n"
+ "test_register_filters (unsigned int%s, unsigned int%s)\n",
+ register_filters.is_empty () ? "" : " mask",
+ register_filters.is_empty () ? "" : " regno");
+ printf ("{\n");
+ if (register_filters.is_empty ())
+ printf (" return true;\n");
+ else
+ {
+ printf (" for (unsigned int id = 0; id < %d; ++id)\n",
+ register_filters.length ());
+ printf (" if ((mask & (1U << id))\n"
+ "\t&& !TEST_REGISTER_FILTER_BIT (id, regno))\n"
+ " return false;\n"
+ " return true;\n");
+ }
+ printf ("}\n"
+ "#endif\n"
+ "\n");
+
if (constraint_max_namelen > 0)
{
write_enum_constraint_num ();
@@ -1548,6 +1676,9 @@ write_tm_preds_h (void)
values.safe_push (std::make_pair (address_end, "CT_FIXED_FORM"));
print_type_tree (values, 0, values.length (), "CT_REGISTER", 2);
puts ("}");
+
+ write_get_register_filter ();
+ write_get_register_filter_id ();
}
puts ("#endif /* tm-preds.h */");
@@ -1599,6 +1730,13 @@ write_insn_preds_c (void)
#include \"tm-constrs.h\"\n\
#include \"target.h\"\n");
+ printf ("\n"
+ "struct target_constraints default_target_constraints;\n"
+ "#if SWITCHABLE_TARGET\n"
+ "struct target_constraints *this_target_constraints"
+ " = &default_target_constraints;\n"
+ "#endif\n");
+
FOR_ALL_PREDICATES (p)
write_one_predicate_function (p);
@@ -1613,6 +1751,8 @@ write_insn_preds_c (void)
if (have_const_int_constraints)
write_insn_const_int_ok_for_constraint ();
}
+
+ write_init_reg_class_start_regs ();
}
/* Argument parsing. */
@@ -400,6 +400,45 @@ process_define_predicate (rtx desc, file_location loc)
#undef I
#undef N
#undef Y
+
+/* Maps register filter conditions to the associated filter identifier. */
+static hash_map<nofree_string_hash, unsigned int> register_filter_map;
+
+/* All register filter conditions, indexed by identifier. */
+vec<const char *> register_filters;
+
+/* Return the unique identifier for filter condition FILTER. Identifiers
+ are assigned automatically when the define_register_constraint is
+ parsed. */
+
+unsigned int
+get_register_filter_id (const char *filter)
+{
+ unsigned int *slot = register_filter_map.get (filter);
+ gcc_assert (slot);
+ return *slot;
+}
+
+/* Process define_register_constraint directive DESC, at location LOC. */
+
+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))
+ {
+ 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);
+ }
+ }
+}
/* Queue PATTERN on LIST_TAIL. Return the address of the new queue
element. */
@@ -1075,10 +1114,15 @@ process_rtx (rtx desc, file_location loc)
case DEFINE_PREDICATE:
case DEFINE_SPECIAL_PREDICATE:
process_define_predicate (desc, loc);
- /* Fall through. */
+ queue_pattern (desc, &define_pred_tail, loc);
+ break;
- case DEFINE_CONSTRAINT:
case DEFINE_REGISTER_CONSTRAINT:
+ process_define_register_constraint (desc, loc);
+ queue_pattern (desc, &define_pred_tail, loc);
+ break;
+
+ case DEFINE_CONSTRAINT:
case DEFINE_MEMORY_CONSTRAINT:
case DEFINE_SPECIAL_MEMORY_CONSTRAINT:
case DEFINE_RELAXED_MEMORY_CONSTRAINT:
@@ -108,6 +108,9 @@ struct optab_def
extern optab_def optabs[];
extern unsigned int num_optabs;
+extern vec<const char *> register_filters;
+extern unsigned int get_register_filter_id (const char *);
+
/* Information about an instruction name that matches an optab pattern. */
struct optab_pattern
{
@@ -140,6 +140,9 @@ reginfo_cc_finalize (void)
CLEAR_HARD_REG_SET (global_reg_set);
}
+/* In insn-preds.cc. */
+extern void init_reg_class_start_regs ();
+
/* Given a register bitmap, turn on the bits in a HARD_REG_SET that
correspond to the hard registers, if any, set in that map. This
could be done far more efficiently by having all sorts of special-cases
@@ -198,6 +201,8 @@ init_reg_sets (void)
SET_HARD_REG_SET (accessible_reg_set);
SET_HARD_REG_SET (operand_reg_set);
+
+ init_reg_class_start_regs ();
}
/* We need to save copies of some of the register information which
@@ -1016,8 +1016,10 @@ DEF_RTL_EXPR(DEFINE_SPECIAL_PREDICATE, "define_special_predicate", "ses", RTX_EX
at -m switches and the like.
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. */
-DEF_RTL_EXPR(DEFINE_REGISTER_CONSTRAINT, "define_register_constraint", "sss", RTX_EXTRA)
+ 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)
/* Definition of a non-register operand constraint. These look at the
operand and decide whether it fits the constraint.
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see
#include "bb-reorder.h"
#include "lower-subreg.h"
#include "function-abi.h"
+#include "tm_p.h"
#if SWITCHABLE_TARGET
class target_globals default_target_globals = {
@@ -60,7 +61,8 @@ class target_globals default_target_globals = {
&default_target_builtins,
&default_target_gcse,
&default_target_bb_reorder,
- &default_target_lower_subreg
+ &default_target_lower_subreg,
+ &default_target_constraints
};
class target_globals *
@@ -84,6 +86,7 @@ save_target_globals (void)
g->gcse = XCNEW (struct target_gcse);
g->bb_reorder = XCNEW (struct target_bb_reorder);
g->lower_subreg = XCNEW (struct target_lower_subreg);
+ g->constraints = XCNEW (target_constraints);
restore_target_globals (g);
init_reg_sets ();
target_reinit ();
@@ -141,6 +144,7 @@ target_globals::~target_globals ()
XDELETE (gcse);
XDELETE (bb_reorder);
XDELETE (lower_subreg);
+ XDELETE (constraints);
}
}
@@ -38,6 +38,7 @@ extern struct target_builtins *this_target_builtins;
extern struct target_gcse *this_target_gcse;
extern struct target_bb_reorder *this_target_bb_reorder;
extern struct target_lower_subreg *this_target_lower_subreg;
+extern struct target_constraints *this_target_constraints;
#endif
class GTY(()) target_globals {
@@ -61,6 +62,7 @@ public:
struct target_gcse *GTY((skip)) gcse;
struct target_bb_reorder *GTY((skip)) bb_reorder;
struct target_lower_subreg *GTY((skip)) lower_subreg;
+ struct target_constraints *GTY((skip)) constraints;
};
#if SWITCHABLE_TARGET
@@ -89,6 +91,7 @@ restore_target_globals (class target_globals *g)
this_target_gcse = g->gcse;
this_target_bb_reorder = g->bb_reorder;
this_target_lower_subreg = g->lower_subreg;
+ this_target_constraints = g->constraints;
}
#endif