diff options
-rw-r--r-- | gcc/doc/options.texi | 25 | ||||
-rw-r--r-- | gcc/opt-functions.awk | 5 | ||||
-rw-r--r-- | gcc/opt-read.awk | 4 | ||||
-rw-r--r-- | gcc/optc-gen.awk | 1 | ||||
-rw-r--r-- | gcc/opts-common.cc | 161 | ||||
-rw-r--r-- | gcc/opts.cc | 44 | ||||
-rw-r--r-- | gcc/opts.h | 12 |
7 files changed, 233 insertions, 19 deletions
diff --git a/gcc/doc/options.texi b/gcc/doc/options.texi index d552b64..9387c82 100644 --- a/gcc/doc/options.texi +++ b/gcc/doc/options.texi @@ -132,6 +132,21 @@ be accepted by the driver. This is used for cases such as @option{-march=native} that are processed by the driver so that @samp{gcc -v} shows how the options chosen depended on the system on which the compiler was run. + +@item Set(@var{number}) +This property is optional, required for enumerations used in +@code{EnumSet} options. @var{number} should be decimal number between +1 and 64 inclusive and divides the enumeration into a set of +sets of mutually exclusive arguments. Arguments with the same +@var{number} can't be specified together in the same option, but +arguments with different @var{number} can. @var{value} needs to be +chosen such that a mask of all @var{value} values from the same set +@var{number} bitwise ored doesn't overlap with masks for other sets. +When @code{-foption=arg_from_set1,arg_from_set4} and +@code{-fno-option=arg_from_set3} are used, the effect is that previous +value of the @code{Var} will get bits from set 1 and 4 masks cleared, +ored @code{Value} of @code{arg_from_set1} and @code{arg_from_set4} +and then will get bits from set 3 mask cleared. @end table @item @@ -396,6 +411,16 @@ with the corresponding @samp{Enum} record. The string is checked and converted to the integer specified in the corresponding @samp{EnumValue} record before being passed to option handlers. +@item EnumSet +Must be used together with the @code{Enum(@var{name})} property. +Corresponding @samp{Enum} record must use @code{Set} properties. +The option's argument is either a string from the set like for +@code{Enum(@var{name})}, but with a slightly different behavior that +the whole @code{Var} isn't overwritten, but only the bits in all the +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 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 198f18c..b2b11be 100644 --- a/gcc/opt-functions.awk +++ b/gcc/opt-functions.awk @@ -297,7 +297,10 @@ function var_set(flags) } if (flag_set_p("Enum.*", flags)) { en = opt_args("Enum", flags); - return enum_index[en] ", CLVC_ENUM, 0" + if (flag_set_p("EnumSet", flags)) + return enum_index[en] ", CLVC_ENUM, 1" + else + return enum_index[en] ", CLVC_ENUM, 0" } if (var_type(flags) == "const char *") return "0, CLVC_STRING, 0" diff --git a/gcc/opt-read.awk b/gcc/opt-read.awk index 6ae4db9..ce3617c 100644 --- a/gcc/opt-read.awk +++ b/gcc/opt-read.awk @@ -97,10 +97,14 @@ BEGIN { enum_name = opt_args_non_empty("Enum", props) string = opt_args_non_empty("String", props) value = opt_args_non_empty("Value", props) + set = opt_args("Set", props) val_flags = "0" val_flags = val_flags \ test_flag("Canonical", props, "| CL_ENUM_CANONICAL") \ test_flag("DriverOnly", props, "| CL_ENUM_DRIVER_ONLY") + if (set != "") + val_flags = val_flags "| ((" set \ + ") << CL_ENUM_SET_SHIFT)" enum_data[enum_name] = enum_data[enum_name] \ " { " quote string quote ", " value ", " val_flags \ " },\n" diff --git a/gcc/optc-gen.awk b/gcc/optc-gen.awk index 8a66126..d7c8e2b 100644 --- a/gcc/optc-gen.awk +++ b/gcc/optc-gen.awk @@ -348,6 +348,7 @@ for (i = 0; i < n_opts; i++) { alias_data = "NULL, NULL, N_OPTS" if (flag_set_p("Enum.*", flags[i])) { if (!flag_set_p("RejectNegative", flags[i]) \ + && !flag_set_p("EnumSet", flags[i]) \ && opts[i] ~ "^[Wfgm]") print "#error Enum allowing negative form" } diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc index 37d2e5f..fb5d953 100644 --- a/gcc/opts-common.cc +++ b/gcc/opts-common.cc @@ -289,26 +289,29 @@ enum_arg_ok_for_language (const struct cl_enum_arg *enum_arg, return (lang_mask & CL_DRIVER) || !(enum_arg->flags & CL_ENUM_DRIVER_ONLY); } -/* Look up ARG in ENUM_ARGS for language LANG_MASK, returning true and - storing the value in *VALUE if found, and returning false without +/* Look up ARG in ENUM_ARGS for language LANG_MASK, returning the cl_enum_arg + index and storing the value in *VALUE if found, and returning -1 without modifying *VALUE if not found. */ -static bool +static int enum_arg_to_value (const struct cl_enum_arg *enum_args, - const char *arg, HOST_WIDE_INT *value, + const char *arg, size_t len, HOST_WIDE_INT *value, unsigned int lang_mask) { unsigned int i; for (i = 0; enum_args[i].arg != NULL; i++) - if (strcmp (arg, enum_args[i].arg) == 0 + if ((len + ? (strncmp (arg, enum_args[i].arg, len) == 0 + && enum_args[i].arg[len] == '\0') + : strcmp (arg, enum_args[i].arg) == 0) && enum_arg_ok_for_language (&enum_args[i], lang_mask)) { *value = enum_args[i].value; - return true; + return i; } - return false; + return -1; } /* Look up ARG in the enum used by option OPT_INDEX for language @@ -324,8 +327,8 @@ opt_enum_arg_to_value (size_t opt_index, const char *arg, gcc_assert (option->var_type == CLVC_ENUM); HOST_WIDE_INT wideval; - if (enum_arg_to_value (cl_enums[option->var_enum].values, arg, - &wideval, lang_mask)) + if (enum_arg_to_value (cl_enums[option->var_enum].values, arg, 0, + &wideval, lang_mask) >= 0) { *value = wideval; return true; @@ -534,7 +537,7 @@ decode_cmdline_option (const char *const *argv, unsigned int lang_mask, { size_t opt_index; const char *arg = 0; - HOST_WIDE_INT value = 1; + HOST_WIDE_INT value = 1, mask = 0; unsigned int result = 1, i, extra_args, separate_args = 0; int adjust_len = 0; size_t total_len; @@ -808,8 +811,56 @@ decode_cmdline_option (const char *const *argv, unsigned int lang_mask, { const struct cl_enum *e = &cl_enums[option->var_enum]; - gcc_assert (value == 1); - if (enum_arg_to_value (e->values, arg, &value, lang_mask)) + gcc_assert (option->var_value || value == 1); + if (option->var_value) + { + const char *p = arg; + HOST_WIDE_INT sum_value = 0; + unsigned HOST_WIDE_INT used_sets = 0; + do + { + const char *q = strchr (p, ','); + HOST_WIDE_INT this_value = 0; + if (q && q == p) + { + errors |= CL_ERR_ENUM_SET_ARG; + break; + } + int idx = enum_arg_to_value (e->values, p, q ? q - p : 0, + &this_value, lang_mask); + if (idx < 0) + { + 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); + + 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; + if (q == NULL) + break; + p = q + 1; + } + while (1); + if (value == 1) + value = sum_value; + else + gcc_checking_assert (value == 0); + } + else if (enum_arg_to_value (e->values, arg, 0, &value, lang_mask) >= 0) { const char *carg = NULL; @@ -825,6 +876,7 @@ decode_cmdline_option (const char *const *argv, unsigned int lang_mask, decoded->opt_index = opt_index; decoded->arg = arg; decoded->value = value; + decoded->mask = mask; decoded->errors = errors; decoded->warn_message = warn_message; @@ -958,6 +1010,7 @@ decode_cmdline_options_to_array (unsigned int argc, const char **argv, opt_array[0].canonical_option[2] = NULL; opt_array[0].canonical_option[3] = NULL; opt_array[0].value = 1; + opt_array[0].mask = 0; opt_array[0].errors = 0; num_decoded_options = 1; @@ -1167,13 +1220,14 @@ handle_option (struct gcc_options *opts, size_t opt_index = decoded->opt_index; const char *arg = decoded->arg; HOST_WIDE_INT value = decoded->value; + HOST_WIDE_INT mask = decoded->mask; const struct cl_option *option = &cl_options[opt_index]; void *flag_var = option_flag_var (opt_index, opts); size_t i; if (flag_var) set_option (opts, (generated_p ? NULL : opts_set), - opt_index, value, arg, kind, loc, dc); + opt_index, value, arg, kind, loc, dc, mask); for (i = 0; i < handlers->num_handlers; i++) if (option->flags & handlers->handlers[i].mask) @@ -1222,6 +1276,7 @@ generate_option (size_t opt_index, const char *arg, HOST_WIDE_INT value, decoded->warn_message = NULL; decoded->arg = arg; decoded->value = value; + decoded->mask = 0; decoded->errors = (option_ok_for_language (option, lang_mask) ? 0 : CL_ERR_WRONG_LANG); @@ -1260,6 +1315,7 @@ generate_option_input_file (const char *file, decoded->canonical_option[2] = NULL; decoded->canonical_option[3] = NULL; decoded->value = 1; + decoded->mask = 0; decoded->errors = 0; } @@ -1342,6 +1398,74 @@ cmdline_handle_error (location_t loc, const struct cl_option *option, return true; } + if (errors & CL_ERR_ENUM_SET_ARG) + { + const struct cl_enum *e = &cl_enums[option->var_enum]; + const char *p = arg; + unsigned HOST_WIDE_INT used_sets = 0; + const char *second_opt = NULL; + size_t second_opt_len = 0; + errors = 0; + do + { + const char *q = strchr (p, ','); + HOST_WIDE_INT this_value = 0; + if (q && q == p) + { + arg = ""; + errors = CL_ERR_ENUM_ARG; + break; + } + int idx = enum_arg_to_value (e->values, p, q ? q - p : 0, + &this_value, lang_mask); + if (idx < 0) + { + if (q == NULL) + q = strchr (p, '\0'); + char *narg = XALLOCAVEC (char, (q - p) + 1); + memcpy (narg, p, q - p); + narg[q - p] = '\0'; + arg = narg; + errors = CL_ERR_ENUM_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) + { + if (q == NULL) + q = strchr (p, '\0'); + if (second_opt == NULL) + { + used_sets = HOST_WIDE_INT_1U << (set - 1); + second_opt = p; + second_opt_len = q - p; + p = arg; + continue; + } + char *args = XALLOCAVEC (char, (q - p) + 1 + second_opt_len + 1); + memcpy (args, p, q - p); + args[q - p] = '\0'; + memcpy (args + (q - p) + 1, second_opt, second_opt_len); + args[(q - p) + 1 + second_opt_len] = '\0'; + error_at (loc, "invalid argument in option %qs", opt); + if (strcmp (args, args + (q - p) + 1) == 0) + inform (loc, "%qs specified multiple times in the same option", + args); + else + inform (loc, "%qs is mutually exclusive with %qs and cannot be" + " specified together", args, args + (q - p) + 1); + return true; + } + used_sets |= HOST_WIDE_INT_1U << (set - 1); + if (q == NULL) + break; + p = q + 1; + } + while (1); + } + if (errors & CL_ERR_ENUM_ARG) { const struct cl_enum *e = &cl_enums[option->var_enum]; @@ -1441,7 +1565,8 @@ read_cmdline_option (struct gcc_options *opts, void set_option (struct gcc_options *opts, struct gcc_options *opts_set, int opt_index, HOST_WIDE_INT value, const char *arg, int kind, - location_t loc, diagnostic_context *dc) + location_t loc, diagnostic_context *dc, + HOST_WIDE_INT mask /* = 0 */) { const struct cl_option *option = &cl_options[opt_index]; void *flag_var = option_flag_var (opt_index, opts); @@ -1550,7 +1675,10 @@ set_option (struct gcc_options *opts, struct gcc_options *opts_set, { const struct cl_enum *e = &cl_enums[option->var_enum]; - e->set (flag_var, value); + if (mask) + e->set (flag_var, value | (e->get (flag_var) & ~mask)); + else + e->set (flag_var, value); if (set_flag_var) e->set (set_flag_var, 1); } @@ -1767,7 +1895,8 @@ control_warning_option (unsigned int opt_index, int kind, const char *arg, { const struct cl_enum *e = &cl_enums[option->var_enum]; - if (enum_arg_to_value (e->values, arg, &value, lang_mask)) + if (enum_arg_to_value (e->values, arg, 0, &value, + lang_mask) >= 0) { const char *carg = NULL; diff --git a/gcc/opts.cc b/gcc/opts.cc index dfdc4d7..733f394 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -3709,12 +3709,56 @@ test_get_option_html_page () #endif } +/* Verify EnumSet 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) + { + const struct cl_enum *e = &cl_enums[cl_options[i].var_enum]; + unsigned HOST_WIDE_INT used_sets = 0; + unsigned HOST_WIDE_INT mask = 0; + unsigned highest_set = 0; + for (unsigned j = 0; e->values[j].arg; ++j) + { + unsigned set = e->values[j].flags >> CL_ENUM_SET_SHIFT; + /* 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 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. */ + if (highest_set == HOST_BITS_PER_WIDE_INT) + ASSERT_TRUE (used_sets == HOST_WIDE_INT_M1U); + else + ASSERT_TRUE (used_sets == (HOST_WIDE_INT_1U << highest_set) - 1); + for (unsigned int j = 1; j <= highest_set; ++j) + { + unsigned HOST_WIDE_INT this_mask = 0; + for (unsigned k = 0; e->values[k].arg; ++k) + { + unsigned set = e->values[j].flags >> CL_ENUM_SET_SHIFT; + if (set == j) + this_mask |= e->values[j].value; + } + ASSERT_TRUE ((mask & this_mask) == 0); + mask |= this_mask; + } + } +} + /* Run all of the selftests within this file. */ void opts_cc_tests () { test_get_option_html_page (); + test_enum_sets (); } } // namespace selftest @@ -170,6 +170,7 @@ extern const unsigned int cl_lang_count; /* Flags for an enumerated option argument. */ #define CL_ENUM_CANONICAL (1 << 0) /* Canonical for this value. */ #define CL_ENUM_DRIVER_ONLY (1 << 1) /* Only accepted in the driver. */ +#define CL_ENUM_SET_SHIFT 2 /* Shift for enum set. */ /* Structure describing an enumerated option argument. */ @@ -226,6 +227,7 @@ extern const unsigned int cl_enums_count; #define CL_ERR_NEGATIVE (1 << 6) /* Negative form of option not permitted (together with OPT_SPECIAL_unknown). */ +#define CL_ERR_ENUM_SET_ARG (1 << 7) /* Bad argument of enumerated set. */ /* Structure describing the result of decoding an option. */ @@ -260,9 +262,14 @@ struct cl_decoded_option /* For a boolean option, 1 for the true case and 0 for the "no-" case. For an unsigned integer option, the value of the - argument. 1 in all other cases. */ + argument. For enum the value of the enumerator corresponding + to argument string. 1 in all other cases. */ HOST_WIDE_INT value; + /* For EnumSet the value mask. Variable should be changed to + value | (prev_value & ~mask). */ + HOST_WIDE_INT mask; + /* Any flags describing errors detected in this option. */ int errors; }; @@ -374,7 +381,8 @@ extern bool get_option_state (struct gcc_options *, int, extern void set_option (struct gcc_options *opts, struct gcc_options *opts_set, int opt_index, HOST_WIDE_INT value, const char *arg, - int kind, location_t loc, diagnostic_context *dc); + int kind, location_t loc, diagnostic_context *dc, + HOST_WIDE_INT = 0); extern void *option_flag_var (int opt_index, struct gcc_options *opts); bool handle_generated_option (struct gcc_options *opts, struct gcc_options *opts_set, |