Improves diagnostic messages for conflicting function multiversioning (FMV) declarations using target_version and/or target_clones attributes.
Conflict errors now include the overlapping version string (if relevant), making it easier to identify and resolve declaration mismatches. gcc/c/ChangeLog: * c-decl.cc (diagnose_mismatched_decls): Add conflicting_ver argument and update diagnostics with it. (duplicate_decls): Ditto. (pushdecl): Add conflicting_version variable update logic to use it. gcc/testsuite/ChangeLog: * gcc.target/aarch64/mv-and-mvc-error1.c: New test. * gcc.target/aarch64/mv-and-mvc-error2.c: New test. * gcc.target/aarch64/mv-and-mvc-error3.c: New test. * gcc.target/aarch64/mv-error1.c: New test. * gcc.target/aarch64/mv-error2.c: New test. * gcc.target/aarch64/mv-error3.c: New test. * gcc.target/aarch64/mv-error4.c: New test. * gcc.target/aarch64/mv-error5.c: New test. * gcc.target/aarch64/mv-error6.c: New test. * gcc.target/aarch64/mv-error7.c: New test. * gcc.target/aarch64/mv-error8.c: New test. * gcc.target/aarch64/mv-error9.c: New test. * gcc.target/aarch64/mvc-error1.c: New test. * gcc.target/aarch64/mvc-error2.c: New test. * gcc.target/aarch64/mvc-warning1.c: New test. --- gcc/c/c-decl.cc | 35 +++++++++++++------ .../gcc.target/aarch64/mv-and-mvc-error1.c | 9 +++++ .../gcc.target/aarch64/mv-and-mvc-error2.c | 9 +++++ .../gcc.target/aarch64/mv-and-mvc-error3.c | 8 +++++ gcc/testsuite/gcc.target/aarch64/mv-error1.c | 18 ++++++++++ gcc/testsuite/gcc.target/aarch64/mv-error2.c | 9 +++++ gcc/testsuite/gcc.target/aarch64/mv-error3.c | 12 +++++++ gcc/testsuite/gcc.target/aarch64/mv-error4.c | 9 +++++ gcc/testsuite/gcc.target/aarch64/mv-error5.c | 8 +++++ gcc/testsuite/gcc.target/aarch64/mv-error6.c | 20 +++++++++++ gcc/testsuite/gcc.target/aarch64/mv-error7.c | 11 ++++++ gcc/testsuite/gcc.target/aarch64/mv-error8.c | 12 +++++++ gcc/testsuite/gcc.target/aarch64/mv-error9.c | 12 +++++++ gcc/testsuite/gcc.target/aarch64/mvc-error1.c | 9 +++++ gcc/testsuite/gcc.target/aarch64/mvc-error2.c | 9 +++++ .../gcc.target/aarch64/mvc-warning1.c | 13 +++++++ 16 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error4.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error5.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error6.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error7.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error8.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error9.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-error1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-error2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-warning1.c
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index e68f8e0cdeb..d5cd31c40a2 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -2119,8 +2119,8 @@ maybe_mark_function_versioned (tree decl) TREE_TYPE (NEWDECL, OLDDECL) respectively. */ static bool -diagnose_mismatched_decls (tree newdecl, tree olddecl, - tree *newtypep, tree *oldtypep) +diagnose_mismatched_decls (tree newdecl, tree olddecl, tree *newtypep, + tree *oldtypep, string_slice *conflicting_ver = NULL) { tree newtype, oldtype; bool retval = true; @@ -2448,7 +2448,12 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, DECL_ATTRIBUTES (newdecl))))) { auto_diagnostic_group d; - error ("redefinition of %q+D", newdecl); + if (conflicting_ver && conflicting_ver->is_valid ()) + error ("redefinition of %qB version for %q+D", + conflicting_ver, + newdecl); + else + error ("redefinition of %q+D", newdecl); locate_old_decl (olddecl); return false; } @@ -3185,7 +3190,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) true. Otherwise, return false. */ static bool -duplicate_decls (tree newdecl, tree olddecl) +duplicate_decls (tree newdecl, tree olddecl, + string_slice *conflicting_ver = NULL) { tree newtype = NULL, oldtype = NULL; @@ -3193,12 +3199,18 @@ duplicate_decls (tree newdecl, tree olddecl) && !mergeable_version_decls (olddecl, newdecl)) { auto_diagnostic_group d; - error ("conflicting versioned declarations of %q+D", newdecl); + if (conflicting_ver && conflicting_ver->is_valid ()) + error ("conflicting %qB version declarations of %q+D", + conflicting_ver, + newdecl); + else + error ("conflicting versioned declarations of %q+D", newdecl); locate_old_decl (olddecl); return false; } - if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype)) + if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype, + conflicting_ver)) { /* Avoid `unused variable' and other warnings for OLDDECL. */ suppress_warning (olddecl, OPT_Wunused); @@ -3385,6 +3397,7 @@ pushdecl (tree x) tree type = TREE_TYPE (x); tree visdecl = b->decl; tree vistype = TREE_TYPE (visdecl); + string_slice conflicting_version = string_slice::invalid (); if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE && COMPLETE_TYPE_P (TREE_TYPE (x))) b->inner_comp = false; @@ -3410,7 +3423,7 @@ pushdecl (tree x) if (b_use && TREE_CODE (b_use->decl) == FUNCTION_DECL && TREE_CODE (x) == FUNCTION_DECL && DECL_FILE_SCOPE_P (b_use->decl) && DECL_FILE_SCOPE_P (x) - && distinct_version_decls (x, b_use->decl) + && distinct_version_decls (x, b_use->decl, &conflicting_version) && comptypes (vistype, type) != 0) { if (current_scope->depth != b->depth) @@ -3429,11 +3442,13 @@ pushdecl (tree x) in the set. */ cgraph_function_version_info *version = b_v; for (; version; version = version->next) - if (!distinct_version_decls (version->this_node->decl, x)) + if (!distinct_version_decls (version->this_node->decl, x, + &conflicting_version)) { /* The decls define overlapping version, so attempt to merge or diagnose the conflict. */ - if (duplicate_decls (x, version->this_node->decl)) + if (duplicate_decls (x, version->this_node->decl, + &conflicting_version)) return version->this_node->decl; else return error_mark_node; @@ -3455,7 +3470,7 @@ pushdecl (tree x) return x; } - if (duplicate_decls (x, b_use->decl)) + if (duplicate_decls (x, b_use->decl, &conflicting_version)) { if (b_use != b) { diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c new file mode 100644 index 00000000000..6a46a053475 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("dotprod"))) int +foo () { return 3; } /* { dg-message "previous definition of .foo." } */ + +__attribute__ ((target_clones ("dotprod", "sve"))) int +foo () { return 1; } /* { dg-error "conflicting .dotprod. version declarations of .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c new file mode 100644 index 00000000000..e34771e029e --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("default"))) int +foo () { return 1; } /* { dg-message "previous definition of .foo." } */ + +__attribute__ ((target_clones ("dotprod", "sve"))) float +foo () { return 3; } /* { dg-error "conflicting versioned declarations of .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c new file mode 100644 index 00000000000..28717a4f89d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +float foo () { return 1; } /* { dg-message "previous definition of .foo." } */ + +__attribute__ ((target_clones ("default", "dotprod", "sve"))) float +foo () { return 3; } /* { dg-error "redefinition of .default. version for .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error1.c b/gcc/testsuite/gcc.target/aarch64/mv-error1.c new file mode 100644 index 00000000000..23d1a0d89f7 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("default"))) int +foo (); + +__attribute__ ((target_version ("default"))) int +foo () { return 1; } /* { dg-message "previous definition of .foo." "note"} */ + +__attribute__ ((target_version ("dotprod"))) float +foo () { return 3; } /* { dg-error "conflicting versioned declarations of .foo." } */ + +__attribute__ ((target_version ("sve"))) int +foo2 () { return 1; } /* { dg-message "previous definition of .foo2." "note" } */ + +__attribute__ ((target_version ("dotprod"))) float +foo2 () { return 3; } /* { dg-error "conflicting versioned declarations of .foo2." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error2.c b/gcc/testsuite/gcc.target/aarch64/mv-error2.c new file mode 100644 index 00000000000..989cc56fc7d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("dotprod"))) float +foo () { return 3; } /* { dg-message "previous definition of .foo." "note" } */ + +__attribute__ ((target_version ("dotprod"))) float +foo () { return 3; } /* { dg-error "redefinition of .dotprod. version for .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error3.c b/gcc/testsuite/gcc.target/aarch64/mv-error3.c new file mode 100644 index 00000000000..e814bac4ef1 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error3.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("dotprod"))) float +foo () { return 3; } + +__attribute__ ((target_version ("default"))) float +foo () { return 3; } /* { dg-message "previous definition of .foo." "note" } */ + +__attribute__ ((target_version ("default"))) float +foo () { return 3; } /* { dg-error "redefinition of .default. version for .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error4.c b/gcc/testsuite/gcc.target/aarch64/mv-error4.c new file mode 100644 index 00000000000..44d3195590d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error4.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("test"))) float +foo () { return 3; } /* { dg-error "invalid feature modifier .test. of value .test. in .target_version. attribute" } */ + +__attribute__ ((target_version ("sve+test"))) float +foo2 () { return 3; } /* { dg-error "invalid feature modifier .test. of value .sve.test. in .target_version. attribute" } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error5.c b/gcc/testsuite/gcc.target/aarch64/mv-error5.c new file mode 100644 index 00000000000..c0b8a9c2b5c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error5.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("sve+sve2"))) int +foo(); /* { dg-message "previous declaration of .foo. with type " } */ + +int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error6.c b/gcc/testsuite/gcc.target/aarch64/mv-error6.c new file mode 100644 index 00000000000..7eecf787a50 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error6.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("sve+sve2"))) int +foo () { /* { dg-message "previous definition of .foo." "note" } */ + return 1; +} + +__attribute__ ((target_version ("sve"))) int +foo () { + return 1; +} + +int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */ + +__attribute__ ((target_version ("sve+sve2"))) int +foo2(); /* { dg-message "previous declaration of .foo2." "note" } */ + +int bar2 () { return foo2 (); } /* { dg-error "implicit declaration of function .foo2." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error7.c b/gcc/testsuite/gcc.target/aarch64/mv-error7.c new file mode 100644 index 00000000000..ccce201cf30 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error7.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("dotprod"))) int +foo (); /* { dg-message "previous declaration of .foo." } */ + +__attribute__ ((target_version ("sve+sve2"))) int +foo (); + +int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error8.c b/gcc/testsuite/gcc.target/aarch64/mv-error8.c new file mode 100644 index 00000000000..7599df189e4 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error8.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("default"))) int +foo (int a, int (*b)[4]) { return 1; } + +int bar(void) { + __attribute__ ((target_version ("dotprod"))) int + foo (int a, int (*b)[5]) { return 3; } /* { dg-error "versioned definitions are only allowed at file scope" } */ + + return 1; +} diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error9.c b/gcc/testsuite/gcc.target/aarch64/mv-error9.c new file mode 100644 index 00000000000..04a2037b98c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mv-error9.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_version ("dotprod"))) int +foo (); /* { dg-message "previous declaration of .foo." } */ + +int +bar () +{ + return foo (); /* { dg-error "implicit declaration of function .foo." } */ +} diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error1.c b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c new file mode 100644 index 00000000000..1f9f557a04b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_clones ("default, dotprod"))) float +foo (); /* { dg-message "previous declaration of .foo." } */ + +__attribute__ ((target_clones ("dotprod", "sve"))) float +foo () { return 3; } /* { dg-error "conflicting .dotprod. version declarations of .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error2.c b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c new file mode 100644 index 00000000000..0c47ea99731 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__ ((target_clones ("default, dotprod"))) float +foo () { return 3; } /* { dg-message "previous definition of .foo." } */ + +__attribute__ ((target_clones ("dotprod", "sve"))) float +foo () { return 3; } /* { dg-error "conflicting .dotprod. version declarations of .foo." } */ diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c new file mode 100644 index 00000000000..bc86f024d84 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0" } */ + +__attribute__((target_clones("default", "dotprod", "sve+sve2"))) +int foo () { + return 1; +} + +__attribute__((target_clones("invalid1"))) +int foo () { /* { dg-warning "invalid .target_clones. version .invalid1. ignored" } */ + return 2; +}