summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/doc/options.texi25
-rw-r--r--gcc/opt-functions.awk5
-rw-r--r--gcc/opt-read.awk4
-rw-r--r--gcc/optc-gen.awk1
-rw-r--r--gcc/opts-common.cc161
-rw-r--r--gcc/opts.cc44
-rw-r--r--gcc/opts.h12
7 files changed, 233 insertions, 19 deletions
diff --git a/gcc/doc/options.texi b/gcc/doc/options.texi
index d552b64d1f4..9387c82cd3d 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 198f18c5660..b2b11bee25e 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 6ae4db9268a..ce3617c8d4a 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 8a66126141a..d7c8e2b80a7 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 37d2e5f20b7..fb5d953968c 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 dfdc4d78389..733f3943871 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
diff --git a/gcc/opts.h b/gcc/opts.h
index b876c5195c9..88fabf9fddc 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -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,