From 0ebb09f5e49c8ca06728bb791415d985df01f6d8 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 24 Jan 2022 11:53:08 +0100 Subject: options: Add EnumBitSet property support [PR104158] On Sat, Jan 22, 2022 at 01:47:08AM +0100, Jakub Jelinek via Gcc-patches wrote: > I think with the 2) patch I achieve what we want for Fortran, for 1) > the only behavior from gcc 11 is that > -fsanitize-coverage=trace-cmp,trace-cmp is now rejected. > This is mainly from the desire to disallow > -fconvert=big-endian,little-endian or -Wbidi-chars=bidirectional,any > etc. where it would be confusing to users what exactly it means. > But it is the only from these options that actually acts as an Enum > bit set, each enumerator can be specified with all the others. > So one option would be stop requiring the EnumSet implies Set properties > must be specified and just require that either they are specified on all > EnumValues, or on none of them; the latter case would be for > -fsanitize-coverage= and the non-Set case would mean that all the > EnumValues need to have disjoint Value bitmasks and that they can > be all specified and unlike the Set case also repeated. > Thoughts on this? Here is an incremental patch to the first two patches of the series that implements EnumBitSet that fully restores the -fsanitize-coverage GCC 11 behavior. 2022-01-24 Jakub Jelinek PR sanitizer/104158 * opt-functions.awk (var_set): Handle EnumBitSet property. * optc-gen.awk: Don't disallow RejectNegative if EnumBitSet is specified. * opts.h (enum cl_enum_var_value): New type. * opts-common.cc (decode_cmdline_option): Use CLEV_* values. Handle CLEV_BITSET. (cmdline_handle_error): Handle CLEV_BITSET. * opts.cc (test_enum_sets): Also test EnumBitSet requirements. * doc/options.texi (EnumBitSet): Document. * common.opt (fsanitize-coverage=): Use EnumBitSet instead of EnumSet. (trace-pc, trace-cmp): Drop Set properties. * gcc.dg/sancov/pr104158-7.c: Adjust for repeating of arguments being allowed. --- gcc/common.opt | 6 ++--- gcc/doc/options.texi | 8 ++++++ gcc/opt-functions.awk | 6 +++-- gcc/optc-gen.awk | 1 + gcc/opts-common.cc | 45 +++++++++++++++++++++++--------- gcc/opts.cc | 15 +++++++++-- gcc/opts.h | 12 +++++++++ gcc/testsuite/gcc.dg/sancov/pr104158-7.c | 10 +++++-- 8 files changed, 81 insertions(+), 22 deletions(-) diff --git a/gcc/common.opt b/gcc/common.opt index 985e6ab6f81..d9233279a16 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1072,17 +1072,17 @@ Common Driver Joined Select what to sanitize. fsanitize-coverage= -Common Joined Enum(sanitize_coverage) Var(flag_sanitize_coverage) EnumSet +Common Joined Enum(sanitize_coverage) Var(flag_sanitize_coverage) EnumBitSet Select type of coverage sanitization. Enum Name(sanitize_coverage) Type(int) EnumValue -Enum(sanitize_coverage) String(trace-pc) Value(SANITIZE_COV_TRACE_PC) Set(1) +Enum(sanitize_coverage) String(trace-pc) Value(SANITIZE_COV_TRACE_PC) EnumValue -Enum(sanitize_coverage) String(trace-cmp) Value(SANITIZE_COV_TRACE_CMP) Set(2) +Enum(sanitize_coverage) String(trace-cmp) Value(SANITIZE_COV_TRACE_CMP) fasan-shadow-offset= Common Joined RejectNegative Var(common_deferred_options) Defer diff --git a/gcc/doc/options.texi b/gcc/doc/options.texi index 9387c82cd3d..50674938e3d 100644 --- a/gcc/doc/options.texi +++ b/gcc/doc/options.texi @@ -421,6 +421,14 @@ enumeration values with the same set bitwise ored together. Or option's argument can be a comma separated list of strings where each string is from a different @code{Set(@var{number})}. +@item EnumBitSet +Must be used together with the @code{Enum(@var{name})} property. +Similar to @samp{EnumSet}, but corresponding @samp{Enum} record must +not use @code{Set} properties, each @code{EnumValue} should have +@code{Value} that is a power of 2, each value is treated as its own +set and its value as the set's mask, so there are no mutually +exclusive arguments. + @item Defer The option should be stored in a vector, specified with @code{Var}, for later processing. diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk index b2b11bee25e..5b0bc6634f5 100644 --- a/gcc/opt-functions.awk +++ b/gcc/opt-functions.awk @@ -298,9 +298,11 @@ function var_set(flags) if (flag_set_p("Enum.*", flags)) { en = opt_args("Enum", flags); if (flag_set_p("EnumSet", flags)) - return enum_index[en] ", CLVC_ENUM, 1" + return enum_index[en] ", CLVC_ENUM, CLEV_SET" + else if (flag_set_p("EnumBitSet", flags)) + return enum_index[en] ", CLVC_ENUM, CLEV_BITSET" else - return enum_index[en] ", CLVC_ENUM, 0" + return enum_index[en] ", CLVC_ENUM, CLEV_NORMAL" } if (var_type(flags) == "const char *") return "0, CLVC_STRING, 0" diff --git a/gcc/optc-gen.awk b/gcc/optc-gen.awk index d7c8e2b80a7..5f7946cf49b 100644 --- a/gcc/optc-gen.awk +++ b/gcc/optc-gen.awk @@ -349,6 +349,7 @@ for (i = 0; i < n_opts; i++) { if (flag_set_p("Enum.*", flags[i])) { if (!flag_set_p("RejectNegative", flags[i]) \ && !flag_set_p("EnumSet", flags[i]) \ + && !flag_set_p("EnumBitSet", flags[i]) \ && opts[i] ~ "^[Wfgm]") print "#error Enum allowing negative form" } diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc index fb5d953968c..1663be1f602 100644 --- a/gcc/opts-common.cc +++ b/gcc/opts-common.cc @@ -811,8 +811,8 @@ decode_cmdline_option (const char *const *argv, unsigned int lang_mask, { const struct cl_enum *e = &cl_enums[option->var_enum]; - gcc_assert (option->var_value || value == 1); - if (option->var_value) + gcc_assert (option->var_value != CLEV_NORMAL || value == 1); + if (option->var_value != CLEV_NORMAL) { const char *p = arg; HOST_WIDE_INT sum_value = 0; @@ -834,19 +834,30 @@ decode_cmdline_option (const char *const *argv, unsigned int lang_mask, break; } - unsigned set = e->values[idx].flags >> CL_ENUM_SET_SHIFT; - gcc_checking_assert (set >= 1 && set <= HOST_BITS_PER_WIDE_INT); - if ((used_sets & (HOST_WIDE_INT_1U << (set - 1))) != 0) + HOST_WIDE_INT this_mask = 0; + if (option->var_value == CLEV_SET) { - errors |= CL_ERR_ENUM_SET_ARG; - break; + unsigned set = e->values[idx].flags >> CL_ENUM_SET_SHIFT; + gcc_checking_assert (set >= 1 + && set <= HOST_BITS_PER_WIDE_INT); + if ((used_sets & (HOST_WIDE_INT_1U << (set - 1))) != 0) + { + errors |= CL_ERR_ENUM_SET_ARG; + break; + } + used_sets |= HOST_WIDE_INT_1U << (set - 1); + + for (int i = 0; e->values[i].arg != NULL; i++) + if (set == (e->values[i].flags >> CL_ENUM_SET_SHIFT)) + this_mask |= e->values[i].value; + } + else + { + gcc_assert (option->var_value == CLEV_BITSET + && ((e->values[idx].flags >> CL_ENUM_SET_SHIFT) + == 0)); + this_mask = this_value; } - used_sets |= HOST_WIDE_INT_1U << (set - 1); - - HOST_WIDE_INT this_mask = 0; - for (int i = 0; e->values[i].arg != NULL; i++) - if (set == (e->values[i].flags >> CL_ENUM_SET_SHIFT)) - this_mask |= e->values[i].value; sum_value |= this_value; mask |= this_mask; @@ -1430,6 +1441,14 @@ cmdline_handle_error (location_t loc, const struct cl_option *option, break; } + if (option->var_value == CLEV_BITSET) + { + if (q == NULL) + break; + p = q + 1; + continue; + } + unsigned set = e->values[idx].flags >> CL_ENUM_SET_SHIFT; gcc_checking_assert (set >= 1 && set <= HOST_BITS_PER_WIDE_INT); if ((used_sets & (HOST_WIDE_INT_1U << (set - 1))) != 0) diff --git a/gcc/opts.cc b/gcc/opts.cc index fb906fa0aea..f21c821ab2e 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -3705,13 +3705,14 @@ test_get_option_html_page () #endif } -/* Verify EnumSet requirements. */ +/* Verify EnumSet and EnumBitSet requirements. */ static void test_enum_sets () { for (unsigned i = 0; i < cl_options_count; ++i) - if (cl_options[i].var_type == CLVC_ENUM && cl_options[i].var_value) + if (cl_options[i].var_type == CLVC_ENUM + && cl_options[i].var_value != CLEV_NORMAL) { const struct cl_enum *e = &cl_enums[cl_options[i].var_enum]; unsigned HOST_WIDE_INT used_sets = 0; @@ -3720,12 +3721,22 @@ test_enum_sets () for (unsigned j = 0; e->values[j].arg; ++j) { unsigned set = e->values[j].flags >> CL_ENUM_SET_SHIFT; + if (cl_options[i].var_value == CLEV_BITSET) + { + /* For EnumBitSet Set shouldn't be used and Value should + be a power of two. */ + ASSERT_TRUE (set == 0); + ASSERT_TRUE (pow2p_hwi (e->values[j].value)); + continue; + } /* Test that enumerators referenced in EnumSet have all Set(n) on them within the valid range. */ ASSERT_TRUE (set >= 1 && set <= HOST_BITS_PER_WIDE_INT); highest_set = MAX (set, highest_set); used_sets |= HOST_WIDE_INT_1U << (set - 1); } + if (cl_options[i].var_value == CLEV_BITSET) + continue; /* If there is just one set, no point to using EnumSet. */ ASSERT_TRUE (highest_set >= 2); /* Test that there are no gaps in between the sets. */ diff --git a/gcc/opts.h b/gcc/opts.h index 88fabf9fddc..a43ce66cffe 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -52,6 +52,18 @@ enum cl_var_type { CLVC_DEFER }; +/* Values for var_value member of CLVC_ENUM. */ +enum cl_enum_var_value { + /* Enum without EnumSet or EnumBitSet. */ + CLEV_NORMAL, + + /* EnumSet. */ + CLEV_SET, + + /* EnumBitSet. */ + CLEV_BITSET +}; + struct cl_option { /* Text of the option, including initial '-'. */ diff --git a/gcc/testsuite/gcc.dg/sancov/pr104158-7.c b/gcc/testsuite/gcc.dg/sancov/pr104158-7.c index aacd23e9512..7fc5b73403a 100644 --- a/gcc/testsuite/gcc.dg/sancov/pr104158-7.c +++ b/gcc/testsuite/gcc.dg/sancov/pr104158-7.c @@ -1,5 +1,11 @@ /* PR sanitizer/104158 */ /* { dg-do compile } */ /* { dg-options "-fsanitize-coverage=trace-cmp,trace-cmp -fdump-tree-optimized" } */ -/* { dg-error "invalid argument in option '-fsanitize-coverage=trace-cmp,trace-cmp'" "" { target *-*-* } 0 } */ -/* { dg-message "'trace-cmp' specified multiple times in the same option" "" { target *-*-* } 0 } */ +/* { dg-final { scan-tree-dump "__sanitizer_cov_trace_cmp" "optimized" } } */ +/* { dg-final { scan-tree-dump-not "__sanitizer_cov_trace_pc" "optimized" } } */ + +int +foo (int a, int b) +{ + return a == b; +} -- cgit v1.2.3