https://gcc.gnu.org/g:9791950017c90c6dd2da773fbc2db98021226981
commit r16-2376-g9791950017c90c6dd2da773fbc2db98021226981 Author: Stefan Schulze Frielinghaus <stefa...@gcc.gnu.org> Date: Mon Jul 21 13:05:26 2025 +0200 genoutput: Verify hard register constraints Since genoutput has no information about hard register names we cannot statically verify those names in constraints of the machine description. Therefore, we have to do it at runtime. Although verification shouldn't be too expensive, restrict it to checking builds. This should be sufficient since hard register constraints in machine descriptions probably change rarely, and each commit should be tested with checking anyway, or at the very least before a release is taken. gcc/ChangeLog: * genoutput.cc (main): Emit function verify_reg_names_in_constraints() for run-time validation. (mdep_constraint_len): Deal with hard register constraints. * output.h (verify_reg_names_in_constraints): New function declaration. * toplev.cc (backend_init): If checking is enabled, call into verify_reg_names_in_constraints(). Diff: --- gcc/genoutput.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ gcc/output.h | 2 ++ gcc/toplev.cc | 4 ++++ 3 files changed, 52 insertions(+) diff --git a/gcc/genoutput.cc b/gcc/genoutput.cc index 1157f820cf4b..183ffc1470ad 100644 --- a/gcc/genoutput.cc +++ b/gcc/genoutput.cc @@ -200,6 +200,8 @@ static const char indep_constraints[] = ",=+%*?!^$#&g"; static class constraint_data * constraints_by_letter_table[1 << CHAR_BIT]; +static hash_set<free_string_hash> used_reg_names; + static int mdep_constraint_len (const char *, file_location, int); static void note_constraint (md_rtx_info *); @@ -1156,6 +1158,45 @@ main (int argc, const char **argv) output_insn_data (); output_get_insn_name (); + /* Since genoutput has no information about hard register names we cannot + statically verify hard register names in constraints of the machine + description. Therefore, we have to do it at runtime. Although + verification shouldn't be too expensive, restrict it to checking builds. + */ + printf ("\n\n#if CHECKING_P\n"); + if (used_reg_names.is_empty ()) + printf ("void verify_reg_names_in_constraints () { }\n"); + else + { + size_t max_len = 0; + for (auto it = used_reg_names.begin (); it != used_reg_names.end (); ++it) + { + size_t len = strlen (*it); + if (len > max_len) + max_len = len; + } + printf ("void\nverify_reg_names_in_constraints ()\n{\n"); + printf (" static const char hregnames[%zu][%zu] = {\n", + used_reg_names.elements (), max_len + 1); + auto it = used_reg_names.begin (); + while (it != used_reg_names.end ()) + { + printf (" \"%s\"", *it); + ++it; + if (it != used_reg_names.end ()) + printf (","); + printf ("\n"); + } + printf (" };\n"); + printf (" for (size_t i = 0; i < %zu; ++i)\n", + used_reg_names.elements ()); + printf (" if (decode_reg_name (hregnames[i]) < 0)\n"); + printf (" internal_error (\"invalid register %%qs used in " + "constraint of machine description\", hregnames[i]);\n"); + printf ("}\n"); + } + printf ("#endif\n"); + fflush (stdout); return (ferror (stdout) != 0 || have_error ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); @@ -1294,6 +1335,11 @@ mdep_constraint_len (const char *s, file_location loc, int opno) ptrdiff_t len = end - s; if (*end == '}' && len > 1 && len < 31) { + char *regname = new char[len]; + memcpy (regname, s + 1, len - 1); + regname[len - 1] = '\0'; + if (used_reg_names.add (regname)) + delete[] regname; return len + 1; } } diff --git a/gcc/output.h b/gcc/output.h index 372d63c5f5c7..0c329ffac3cd 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -639,4 +639,6 @@ extern int default_address_cost (rtx, machine_mode, addr_space_t, bool); /* Stack usage. */ extern void output_stack_usage (void); +extern void verify_reg_names_in_constraints (); + #endif /* ! GCC_OUTPUT_H */ diff --git a/gcc/toplev.cc b/gcc/toplev.cc index 00a8ccb7a692..931466ca6324 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -1815,6 +1815,10 @@ backend_init_target (void) static void backend_init (void) { +#if CHECKING_P + verify_reg_names_in_constraints (); +#endif + init_emit_once (); init_rtlanal ();