aboutsummaryrefslogtreecommitdiff
path: root/gcc/opts-common.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/opts-common.cc')
-rw-r--r--gcc/opts-common.cc1857
1 files changed, 1857 insertions, 0 deletions
diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc
new file mode 100644
index 0000000..0ce0937
--- /dev/null
+++ b/gcc/opts-common.cc
@@ -0,0 +1,1857 @@
+/* Command line option handling.
+ Copyright (C) 2006-2022 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "intl.h"
+#include "coretypes.h"
+#include "opts.h"
+#include "options.h"
+#include "diagnostic.h"
+#include "spellcheck.h"
+
+static void prune_options (struct cl_decoded_option **, unsigned int *);
+
+/* An option that is undocumented, that takes a joined argument, and
+ that doesn't fit any of the classes of uses (language/common,
+ driver, target) is assumed to be a prefix used to catch
+ e.g. negated options, and stop them from being further shortened to
+ a prefix that could use the negated option as an argument. For
+ example, we want -gno-statement-frontiers to be taken as a negation
+ of -gstatement-frontiers, but without catching the gno- prefix and
+ signaling it's to be used for option remapping, it would end up
+ backtracked to g with no-statemnet-frontiers as the debug level. */
+
+static bool
+remapping_prefix_p (const struct cl_option *opt)
+{
+ return opt->flags & CL_UNDOCUMENTED
+ && opt->flags & CL_JOINED
+ && !(opt->flags & (CL_DRIVER | CL_TARGET | CL_COMMON | CL_LANG_ALL));
+}
+
+/* Perform a binary search to find which option the command-line INPUT
+ matches. Returns its index in the option array, and
+ OPT_SPECIAL_unknown on failure.
+
+ This routine is quite subtle. A normal binary search is not good
+ enough because some options can be suffixed with an argument, and
+ multiple sub-matches can occur, e.g. input of "-pedantic" matching
+ the initial substring of "-pedantic-errors".
+
+ A more complicated example is -gstabs. It should match "-g" with
+ an argument of "stabs". Suppose, however, that the number and list
+ of switches are such that the binary search tests "-gen-decls"
+ before having tested "-g". This doesn't match, and as "-gen-decls"
+ is less than "-gstabs", it will become the lower bound of the
+ binary search range, and "-g" will never be seen. To resolve this
+ issue, 'optc-gen.awk' makes "-gen-decls" point, via the back_chain member,
+ to "-g" so that failed searches that end between "-gen-decls" and
+ the lexicographically subsequent switch know to go back and see if
+ "-g" causes a match (which it does in this example).
+
+ This search is done in such a way that the longest match for the
+ front end in question wins. If there is no match for the current
+ front end, the longest match for a different front end is returned
+ (or N_OPTS if none) and the caller emits an error message. */
+size_t
+find_opt (const char *input, unsigned int lang_mask)
+{
+ size_t mn, mn_orig, mx, md, opt_len;
+ size_t match_wrong_lang;
+ int comp;
+
+ mn = 0;
+ mx = cl_options_count;
+
+ /* Find mn such this lexicographical inequality holds:
+ cl_options[mn] <= input < cl_options[mn + 1]. */
+ while (mx - mn > 1)
+ {
+ md = (mn + mx) / 2;
+ opt_len = cl_options[md].opt_len;
+ comp = strncmp (input, cl_options[md].opt_text + 1, opt_len);
+
+ if (comp < 0)
+ mx = md;
+ else
+ mn = md;
+ }
+
+ mn_orig = mn;
+
+ /* This is the switch that is the best match but for a different
+ front end, or OPT_SPECIAL_unknown if there is no match at all. */
+ match_wrong_lang = OPT_SPECIAL_unknown;
+
+ /* Backtrace the chain of possible matches, returning the longest
+ one, if any, that fits best. With current GCC switches, this
+ loop executes at most twice. */
+ do
+ {
+ const struct cl_option *opt = &cl_options[mn];
+
+ /* Is the input either an exact match or a prefix that takes a
+ joined argument? */
+ if (!strncmp (input, opt->opt_text + 1, opt->opt_len)
+ && (input[opt->opt_len] == '\0' || (opt->flags & CL_JOINED)))
+ {
+ /* If language is OK, return it. */
+ if (opt->flags & lang_mask)
+ return mn;
+
+ if (remapping_prefix_p (opt))
+ return OPT_SPECIAL_unknown;
+
+ /* If we haven't remembered a prior match, remember this
+ one. Any prior match is necessarily better. */
+ if (match_wrong_lang == OPT_SPECIAL_unknown)
+ match_wrong_lang = mn;
+ }
+
+ /* Try the next possibility. This is cl_options_count if there
+ are no more. */
+ mn = opt->back_chain;
+ }
+ while (mn != cl_options_count);
+
+ if (match_wrong_lang == OPT_SPECIAL_unknown && input[0] == '-')
+ {
+ /* Long options, starting "--", may be abbreviated if the
+ abbreviation is unambiguous. This only applies to options
+ not taking a joined argument, and abbreviations of "--option"
+ are permitted even if there is a variant "--option=". */
+ size_t mnc = mn_orig + 1;
+ size_t cmp_len = strlen (input);
+ while (mnc < cl_options_count
+ && strncmp (input, cl_options[mnc].opt_text + 1, cmp_len) == 0)
+ {
+ /* Option matching this abbreviation. OK if it is the first
+ match and that does not take a joined argument, or the
+ second match, taking a joined argument and with only '='
+ added to the first match; otherwise considered
+ ambiguous. */
+ if (mnc == mn_orig + 1
+ && !(cl_options[mnc].flags & CL_JOINED))
+ match_wrong_lang = mnc;
+ else if (mnc == mn_orig + 2
+ && match_wrong_lang == mn_orig + 1
+ && (cl_options[mnc].flags & CL_JOINED)
+ && (cl_options[mnc].opt_len
+ == cl_options[mn_orig + 1].opt_len + 1)
+ && strncmp (cl_options[mnc].opt_text + 1,
+ cl_options[mn_orig + 1].opt_text + 1,
+ cl_options[mn_orig + 1].opt_len) == 0)
+ ; /* OK, as long as there are no more matches. */
+ else
+ return OPT_SPECIAL_unknown;
+ mnc++;
+ }
+ }
+
+ /* Return the best wrong match, or OPT_SPECIAL_unknown if none. */
+ return match_wrong_lang;
+}
+
+/* If ARG is a non-negative decimal or hexadecimal integer representable
+ in HOST_WIDE_INT return its value, otherwise return -1. If ERR is not
+ null set *ERR to zero on success or to EINVAL or to the value of errno
+ otherwise. */
+
+HOST_WIDE_INT
+integral_argument (const char *arg, int *err, bool byte_size_suffix)
+{
+ if (!err)
+ err = &errno;
+
+ if (!ISDIGIT (*arg))
+ {
+ *err = EINVAL;
+ return -1;
+ }
+
+ *err = 0;
+ errno = 0;
+
+ char *end = NULL;
+ unsigned HOST_WIDE_INT unit = 1;
+ unsigned HOST_WIDE_INT value = strtoull (arg, &end, 10);
+
+ /* If the value is too large to be represented use the maximum
+ representable value that strtoull sets VALUE to (setting
+ errno to ERANGE). */
+
+ if (end && *end)
+ {
+ if (!byte_size_suffix)
+ {
+ errno = 0;
+ value = strtoull (arg, &end, 0);
+ if (*end)
+ {
+ if (errno)
+ *err = errno;
+ else
+ *err = EINVAL;
+ return -1;
+ }
+
+ return value;
+ }
+
+ /* Numeric option arguments are at most INT_MAX. Make it
+ possible to specify a larger value by accepting common
+ suffixes. */
+ if (!strcmp (end, "kB"))
+ unit = 1000;
+ else if (!strcasecmp (end, "KiB") || !strcmp (end, "KB"))
+ unit = 1024;
+ else if (!strcmp (end, "MB"))
+ unit = HOST_WIDE_INT_UC (1000) * 1000;
+ else if (!strcasecmp (end, "MiB"))
+ unit = HOST_WIDE_INT_UC (1024) * 1024;
+ else if (!strcasecmp (end, "GB"))
+ unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000;
+ else if (!strcasecmp (end, "GiB"))
+ unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024;
+ else if (!strcasecmp (end, "TB"))
+ unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000;
+ else if (!strcasecmp (end, "TiB"))
+ unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024;
+ else if (!strcasecmp (end, "PB"))
+ unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000 * 1000;
+ else if (!strcasecmp (end, "PiB"))
+ unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024 * 1024;
+ else if (!strcasecmp (end, "EB"))
+ unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000 * 1000
+ * 1000;
+ else if (!strcasecmp (end, "EiB"))
+ unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024 * 1024
+ * 1024;
+ else
+ {
+ /* This could mean an unknown suffix or a bad prefix, like
+ "+-1". */
+ *err = EINVAL;
+ return -1;
+ }
+ }
+
+ if (unit)
+ {
+ unsigned HOST_WIDE_INT prod = value * unit;
+ value = prod < value ? HOST_WIDE_INT_M1U : prod;
+ }
+
+ return value;
+}
+
+/* Return whether OPTION is OK for the language given by
+ LANG_MASK. */
+static bool
+option_ok_for_language (const struct cl_option *option,
+ unsigned int lang_mask)
+{
+ if (!(option->flags & lang_mask))
+ return false;
+ else if ((option->flags & CL_TARGET)
+ && (option->flags & (CL_LANG_ALL | CL_DRIVER))
+ && !(option->flags & (lang_mask & ~CL_COMMON & ~CL_TARGET)))
+ /* Complain for target flag language mismatches if any languages
+ are specified. */
+ return false;
+ return true;
+}
+
+/* Return whether ENUM_ARG is OK for the language given by
+ LANG_MASK. */
+
+static bool
+enum_arg_ok_for_language (const struct cl_enum_arg *enum_arg,
+ unsigned int lang_mask)
+{
+ 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
+ modifying *VALUE if not found. */
+
+static bool
+enum_arg_to_value (const struct cl_enum_arg *enum_args,
+ const char *arg, 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
+ && enum_arg_ok_for_language (&enum_args[i], lang_mask))
+ {
+ *value = enum_args[i].value;
+ return true;
+ }
+
+ return false;
+}
+
+/* Look up ARG in the enum used by option OPT_INDEX for language
+ LANG_MASK, returning true and storing the value in *VALUE if found,
+ and returning false without modifying *VALUE if not found. */
+
+bool
+opt_enum_arg_to_value (size_t opt_index, const char *arg,
+ int *value, unsigned int lang_mask)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+
+ 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))
+ {
+ *value = wideval;
+ return true;
+ }
+
+ return false;
+}
+
+/* Look of VALUE in ENUM_ARGS for language LANG_MASK and store the
+ corresponding string in *ARGP, returning true if the found string
+ was marked as canonical, false otherwise. If VALUE is not found
+ (which may be the case for uninitialized values if the relevant
+ option has not been passed), set *ARGP to NULL and return
+ false. */
+
+bool
+enum_value_to_arg (const struct cl_enum_arg *enum_args,
+ const char **argp, int value, unsigned int lang_mask)
+{
+ unsigned int i;
+
+ for (i = 0; enum_args[i].arg != NULL; i++)
+ if (enum_args[i].value == value
+ && (enum_args[i].flags & CL_ENUM_CANONICAL)
+ && enum_arg_ok_for_language (&enum_args[i], lang_mask))
+ {
+ *argp = enum_args[i].arg;
+ return true;
+ }
+
+ for (i = 0; enum_args[i].arg != NULL; i++)
+ if (enum_args[i].value == value
+ && enum_arg_ok_for_language (&enum_args[i], lang_mask))
+ {
+ *argp = enum_args[i].arg;
+ return false;
+ }
+
+ *argp = NULL;
+ return false;
+}
+
+/* Fill in the canonical option part of *DECODED with an option
+ described by OPT_INDEX, ARG and VALUE. */
+
+static void
+generate_canonical_option (size_t opt_index, const char *arg,
+ HOST_WIDE_INT value,
+ struct cl_decoded_option *decoded)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+ const char *opt_text = option->opt_text;
+
+ if (value == 0
+ && !option->cl_reject_negative
+ && (opt_text[1] == 'W' || opt_text[1] == 'f'
+ || opt_text[1] == 'g' || opt_text[1] == 'm'))
+ {
+ char *t = XOBNEWVEC (&opts_obstack, char, option->opt_len + 5);
+ t[0] = '-';
+ t[1] = opt_text[1];
+ t[2] = 'n';
+ t[3] = 'o';
+ t[4] = '-';
+ memcpy (t + 5, opt_text + 2, option->opt_len);
+ opt_text = t;
+ }
+
+ decoded->canonical_option[2] = NULL;
+ decoded->canonical_option[3] = NULL;
+
+ if (arg)
+ {
+ if ((option->flags & CL_SEPARATE)
+ && !option->cl_separate_alias)
+ {
+ decoded->canonical_option[0] = opt_text;
+ decoded->canonical_option[1] = arg;
+ decoded->canonical_option_num_elements = 2;
+ }
+ else
+ {
+ gcc_assert (option->flags & CL_JOINED);
+ decoded->canonical_option[0] = opts_concat (opt_text, arg, NULL);
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option_num_elements = 1;
+ }
+ }
+ else
+ {
+ decoded->canonical_option[0] = opt_text;
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option_num_elements = 1;
+ }
+}
+
+/* Structure describing mappings from options on the command line to
+ options to look up with find_opt. */
+struct option_map
+{
+ /* Prefix of the option on the command line. */
+ const char *opt0;
+ /* If two argv elements are considered to be merged into one option,
+ prefix for the second element, otherwise NULL. */
+ const char *opt1;
+ /* The new prefix to map to. */
+ const char *new_prefix;
+ /* Whether at least one character is needed following opt1 or opt0
+ for this mapping to be used. (--optimize= is valid for -O, but
+ --warn- is not valid for -W.) */
+ bool another_char_needed;
+ /* Whether the original option is a negated form of the option
+ resulting from this map. */
+ bool negated;
+};
+static const struct option_map option_map[] =
+ {
+ { "-Wno-", NULL, "-W", false, true },
+ { "-fno-", NULL, "-f", false, true },
+ { "-gno-", NULL, "-g", false, true },
+ { "-mno-", NULL, "-m", false, true },
+ { "--debug=", NULL, "-g", false, false },
+ { "--machine-", NULL, "-m", true, false },
+ { "--machine-no-", NULL, "-m", false, true },
+ { "--machine=", NULL, "-m", false, false },
+ { "--machine=no-", NULL, "-m", false, true },
+ { "--machine", "", "-m", false, false },
+ { "--machine", "no-", "-m", false, true },
+ { "--optimize=", NULL, "-O", false, false },
+ { "--std=", NULL, "-std=", false, false },
+ { "--std", "", "-std=", false, false },
+ { "--warn-", NULL, "-W", true, false },
+ { "--warn-no-", NULL, "-W", false, true },
+ { "--", NULL, "-f", true, false },
+ { "--no-", NULL, "-f", false, true }
+ };
+
+/* Helper function for gcc.c's driver::suggest_option, for populating the
+ vec of suggestions for misspelled options.
+
+ option_map above provides various prefixes for spelling command-line
+ options, which decode_cmdline_option uses to map spellings of options
+ to specific options. We want to do the reverse: to find all the ways
+ that a user could validly spell an option.
+
+ Given valid OPT_TEXT (with a leading dash) for OPTION, add it and all
+ of its valid variant spellings to CANDIDATES, each without a leading
+ dash.
+
+ For example, given "-Wabi-tag", the following are added to CANDIDATES:
+ "Wabi-tag"
+ "Wno-abi-tag"
+ "-warn-abi-tag"
+ "-warn-no-abi-tag".
+
+ The added strings must be freed using free. */
+
+void
+add_misspelling_candidates (auto_vec<char *> *candidates,
+ const struct cl_option *option,
+ const char *opt_text)
+{
+ gcc_assert (candidates);
+ gcc_assert (option);
+ gcc_assert (opt_text);
+ if (remapping_prefix_p (option))
+ return;
+ candidates->safe_push (xstrdup (opt_text + 1));
+ for (unsigned i = 0; i < ARRAY_SIZE (option_map); i++)
+ {
+ const char *opt0 = option_map[i].opt0;
+ const char *new_prefix = option_map[i].new_prefix;
+ size_t new_prefix_len = strlen (new_prefix);
+
+ if (option->cl_reject_negative && option_map[i].negated)
+ continue;
+
+ if (strncmp (opt_text, new_prefix, new_prefix_len) == 0)
+ {
+ char *alternative = concat (opt0 + 1, opt_text + new_prefix_len,
+ NULL);
+ candidates->safe_push (alternative);
+ }
+ }
+
+ /* For all params (e.g. --param=key=value),
+ include also '--param key=value'. */
+ const char *prefix = "--param=";
+ if (strstr (opt_text, prefix) == opt_text)
+ {
+ char *param = xstrdup (opt_text + 1);
+ gcc_assert (param[6] == '=');
+ param[6] = ' ';
+ candidates->safe_push (param);
+ }
+}
+
+/* Decode the switch beginning at ARGV for the language indicated by
+ LANG_MASK (including CL_COMMON and CL_TARGET if applicable), into
+ the structure *DECODED. Returns the number of switches
+ consumed. */
+
+static unsigned int
+decode_cmdline_option (const char *const *argv, unsigned int lang_mask,
+ struct cl_decoded_option *decoded)
+{
+ size_t opt_index;
+ const char *arg = 0;
+ HOST_WIDE_INT value = 1;
+ unsigned int result = 1, i, extra_args, separate_args = 0;
+ int adjust_len = 0;
+ size_t total_len;
+ char *p;
+ const struct cl_option *option;
+ int errors = 0;
+ const char *warn_message = NULL;
+ bool separate_arg_flag;
+ bool joined_arg_flag;
+ bool have_separate_arg = false;
+
+ extra_args = 0;
+
+ const char *opt_value = argv[0] + 1;
+ opt_index = find_opt (opt_value, lang_mask);
+ i = 0;
+ while (opt_index == OPT_SPECIAL_unknown
+ && i < ARRAY_SIZE (option_map))
+ {
+ const char *opt0 = option_map[i].opt0;
+ const char *opt1 = option_map[i].opt1;
+ const char *new_prefix = option_map[i].new_prefix;
+ bool another_char_needed = option_map[i].another_char_needed;
+ size_t opt0_len = strlen (opt0);
+ size_t opt1_len = (opt1 == NULL ? 0 : strlen (opt1));
+ size_t optn_len = (opt1 == NULL ? opt0_len : opt1_len);
+ size_t new_prefix_len = strlen (new_prefix);
+
+ extra_args = (opt1 == NULL ? 0 : 1);
+ value = !option_map[i].negated;
+
+ if (strncmp (argv[0], opt0, opt0_len) == 0
+ && (opt1 == NULL
+ || (argv[1] != NULL && strncmp (argv[1], opt1, opt1_len) == 0))
+ && (!another_char_needed
+ || argv[extra_args][optn_len] != 0))
+ {
+ size_t arglen = strlen (argv[extra_args]);
+ char *dup;
+
+ adjust_len = (int) optn_len - (int) new_prefix_len;
+ dup = XNEWVEC (char, arglen + 1 - adjust_len);
+ memcpy (dup, new_prefix, new_prefix_len);
+ memcpy (dup + new_prefix_len, argv[extra_args] + optn_len,
+ arglen - optn_len + 1);
+ opt_index = find_opt (dup + 1, lang_mask);
+ free (dup);
+ }
+ i++;
+ }
+
+ if (opt_index == OPT_SPECIAL_unknown)
+ {
+ arg = argv[0];
+ extra_args = 0;
+ value = 1;
+ goto done;
+ }
+
+ option = &cl_options[opt_index];
+
+ /* Reject negative form of switches that don't take negatives as
+ unrecognized. */
+ if (!value && option->cl_reject_negative)
+ {
+ opt_index = OPT_SPECIAL_unknown;
+ errors |= CL_ERR_NEGATIVE;
+ arg = argv[0];
+ goto done;
+ }
+
+ /* Clear the initial value for size options (it will be overwritten
+ later based on the Init(value) specification in the opt file. */
+ if (option->var_type == CLVC_SIZE)
+ value = 0;
+
+ result = extra_args + 1;
+ warn_message = option->warn_message;
+
+ /* Check to see if the option is disabled for this configuration. */
+ if (option->cl_disabled)
+ errors |= CL_ERR_DISABLED;
+
+ /* Determine whether there may be a separate argument based on
+ whether this option is being processed for the driver, and, if
+ so, how many such arguments. */
+ separate_arg_flag = ((option->flags & CL_SEPARATE)
+ && !(option->cl_no_driver_arg
+ && (lang_mask & CL_DRIVER)));
+ separate_args = (separate_arg_flag
+ ? option->cl_separate_nargs + 1
+ : 0);
+ joined_arg_flag = (option->flags & CL_JOINED) != 0;
+
+ /* Sort out any argument the switch takes. */
+ if (joined_arg_flag)
+ {
+ /* Have arg point to the original switch. This is because
+ some code, such as disable_builtin_function, expects its
+ argument to be persistent until the program exits. */
+ arg = argv[extra_args] + cl_options[opt_index].opt_len + 1 + adjust_len;
+
+ if (*arg == '\0' && !option->cl_missing_ok)
+ {
+ if (separate_arg_flag)
+ {
+ arg = argv[extra_args + 1];
+ result = extra_args + 2;
+ if (arg == NULL)
+ result = extra_args + 1;
+ else
+ have_separate_arg = true;
+ }
+ else
+ /* Missing argument. */
+ arg = NULL;
+ }
+ }
+ else if (separate_arg_flag)
+ {
+ arg = argv[extra_args + 1];
+ for (i = 0; i < separate_args; i++)
+ if (argv[extra_args + 1 + i] == NULL)
+ {
+ errors |= CL_ERR_MISSING_ARG;
+ break;
+ }
+ result = extra_args + 1 + i;
+ if (arg != NULL)
+ have_separate_arg = true;
+ }
+
+ if (arg == NULL && (separate_arg_flag || joined_arg_flag))
+ errors |= CL_ERR_MISSING_ARG;
+
+ /* Is this option an alias (or an ignored option, marked as an alias
+ of OPT_SPECIAL_ignore)? */
+ if (option->alias_target != N_OPTS
+ && (!option->cl_separate_alias || have_separate_arg))
+ {
+ size_t new_opt_index = option->alias_target;
+
+ if (new_opt_index == OPT_SPECIAL_ignore
+ || new_opt_index == OPT_SPECIAL_warn_removed)
+ {
+ gcc_assert (option->alias_arg == NULL);
+ gcc_assert (option->neg_alias_arg == NULL);
+ opt_index = new_opt_index;
+ arg = NULL;
+ }
+ else
+ {
+ const struct cl_option *new_option = &cl_options[new_opt_index];
+
+ /* The new option must not be an alias itself. */
+ gcc_assert (new_option->alias_target == N_OPTS
+ || new_option->cl_separate_alias);
+
+ if (option->neg_alias_arg)
+ {
+ gcc_assert (option->alias_arg != NULL);
+ gcc_assert (arg == NULL);
+ gcc_assert (!option->cl_negative_alias);
+ if (value)
+ arg = option->alias_arg;
+ else
+ arg = option->neg_alias_arg;
+ value = 1;
+ }
+ else if (option->alias_arg)
+ {
+ gcc_assert (value == 1);
+ gcc_assert (arg == NULL);
+ gcc_assert (!option->cl_negative_alias);
+ arg = option->alias_arg;
+ }
+
+ if (option->cl_negative_alias)
+ value = !value;
+
+ opt_index = new_opt_index;
+ option = new_option;
+
+ if (value == 0)
+ gcc_assert (!option->cl_reject_negative);
+
+ /* Recompute what arguments are allowed. */
+ separate_arg_flag = ((option->flags & CL_SEPARATE)
+ && !(option->cl_no_driver_arg
+ && (lang_mask & CL_DRIVER)));
+ joined_arg_flag = (option->flags & CL_JOINED) != 0;
+
+ if (separate_args > 1 || option->cl_separate_nargs)
+ gcc_assert (separate_args
+ == (unsigned int) option->cl_separate_nargs + 1);
+
+ if (!(errors & CL_ERR_MISSING_ARG))
+ {
+ if (separate_arg_flag || joined_arg_flag)
+ {
+ if (option->cl_missing_ok && arg == NULL)
+ arg = "";
+ gcc_assert (arg != NULL);
+ }
+ else
+ gcc_assert (arg == NULL);
+ }
+
+ /* Recheck for warnings and disabled options. */
+ if (option->warn_message)
+ {
+ gcc_assert (warn_message == NULL);
+ warn_message = option->warn_message;
+ }
+ if (option->cl_disabled)
+ errors |= CL_ERR_DISABLED;
+ }
+ }
+
+ /* Check if this is a switch for a different front end. */
+ if (!option_ok_for_language (option, lang_mask))
+ errors |= CL_ERR_WRONG_LANG;
+ else if (strcmp (option->opt_text, "-Werror=") == 0
+ && strchr (opt_value, ',') == NULL)
+ {
+ /* Verify that -Werror argument is a valid warning
+ for a language. */
+ char *werror_arg = xstrdup (opt_value + 6);
+ werror_arg[0] = 'W';
+
+ size_t warning_index = find_opt (werror_arg, lang_mask);
+ free (werror_arg);
+ if (warning_index != OPT_SPECIAL_unknown)
+ {
+ const struct cl_option *warning_option
+ = &cl_options[warning_index];
+ if (!option_ok_for_language (warning_option, lang_mask))
+ errors |= CL_ERR_WRONG_LANG;
+ }
+ }
+
+ /* Convert the argument to lowercase if appropriate. */
+ if (arg && option->cl_tolower)
+ {
+ size_t j;
+ size_t len = strlen (arg);
+ char *arg_lower = XOBNEWVEC (&opts_obstack, char, len + 1);
+
+ for (j = 0; j < len; j++)
+ arg_lower[j] = TOLOWER ((unsigned char) arg[j]);
+ arg_lower[len] = 0;
+ arg = arg_lower;
+ }
+
+ /* If the switch takes an integer argument, convert it. */
+ if (arg && (option->cl_uinteger || option->cl_host_wide_int))
+ {
+ int error = 0;
+ value = *arg ? integral_argument (arg, &error, option->cl_byte_size) : 0;
+ if (error)
+ errors |= CL_ERR_UINT_ARG;
+
+ /* Reject value out of a range. */
+ if (option->range_max != -1
+ && (value < option->range_min || value > option->range_max))
+ errors |= CL_ERR_INT_RANGE_ARG;
+ }
+
+ /* If the switch takes an enumerated argument, convert it. */
+ if (arg && (option->var_type == CLVC_ENUM))
+ {
+ 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))
+ {
+ const char *carg = NULL;
+
+ if (enum_value_to_arg (e->values, &carg, value, lang_mask))
+ arg = carg;
+ gcc_assert (carg != NULL);
+ }
+ else
+ errors |= CL_ERR_ENUM_ARG;
+ }
+
+ done:
+ decoded->opt_index = opt_index;
+ decoded->arg = arg;
+ decoded->value = value;
+ decoded->errors = errors;
+ decoded->warn_message = warn_message;
+
+ if (opt_index == OPT_SPECIAL_unknown)
+ gcc_assert (result == 1);
+
+ gcc_assert (result >= 1 && result <= ARRAY_SIZE (decoded->canonical_option));
+ decoded->canonical_option_num_elements = result;
+ total_len = 0;
+ for (i = 0; i < ARRAY_SIZE (decoded->canonical_option); i++)
+ {
+ if (i < result)
+ {
+ size_t len;
+ if (opt_index == OPT_SPECIAL_unknown)
+ decoded->canonical_option[i] = argv[i];
+ else
+ decoded->canonical_option[i] = NULL;
+ len = strlen (argv[i]);
+ /* If the argument is an empty string, we will print it as "" in
+ orig_option_with_args_text. */
+ total_len += (len != 0 ? len : 2) + 1;
+ }
+ else
+ decoded->canonical_option[i] = NULL;
+ }
+ if (opt_index != OPT_SPECIAL_unknown && opt_index != OPT_SPECIAL_ignore
+ && opt_index != OPT_SPECIAL_warn_removed)
+ {
+ generate_canonical_option (opt_index, arg, value, decoded);
+ if (separate_args > 1)
+ {
+ for (i = 0; i < separate_args; i++)
+ {
+ if (argv[extra_args + 1 + i] == NULL)
+ break;
+ else
+ decoded->canonical_option[1 + i] = argv[extra_args + 1 + i];
+ }
+ gcc_assert (result == 1 + i);
+ decoded->canonical_option_num_elements = result;
+ }
+ }
+ decoded->orig_option_with_args_text
+ = p = XOBNEWVEC (&opts_obstack, char, total_len);
+ for (i = 0; i < result; i++)
+ {
+ size_t len = strlen (argv[i]);
+
+ /* Print the empty string verbally. */
+ if (len == 0)
+ {
+ *p++ = '"';
+ *p++ = '"';
+ }
+ else
+ memcpy (p, argv[i], len);
+ p += len;
+ if (i == result - 1)
+ *p++ = 0;
+ else
+ *p++ = ' ';
+ }
+
+ return result;
+}
+
+/* Obstack for option strings. */
+
+struct obstack opts_obstack;
+
+/* Like libiberty concat, but allocate using opts_obstack. */
+
+char *
+opts_concat (const char *first, ...)
+{
+ char *newstr, *end;
+ size_t length = 0;
+ const char *arg;
+ va_list ap;
+
+ /* First compute the size of the result and get sufficient memory. */
+ va_start (ap, first);
+ for (arg = first; arg; arg = va_arg (ap, const char *))
+ length += strlen (arg);
+ newstr = XOBNEWVEC (&opts_obstack, char, length + 1);
+ va_end (ap);
+
+ /* Now copy the individual pieces to the result string. */
+ va_start (ap, first);
+ for (arg = first, end = newstr; arg; arg = va_arg (ap, const char *))
+ {
+ length = strlen (arg);
+ memcpy (end, arg, length);
+ end += length;
+ }
+ *end = '\0';
+ va_end (ap);
+ return newstr;
+}
+
+/* Decode command-line options (ARGC and ARGV being the arguments of
+ main) into an array, setting *DECODED_OPTIONS to a pointer to that
+ array and *DECODED_OPTIONS_COUNT to the number of entries in the
+ array. The first entry in the array is always one for the program
+ name (OPT_SPECIAL_program_name). LANG_MASK indicates the language
+ flags applicable for decoding (including CL_COMMON and CL_TARGET if
+ those options should be considered applicable). Do not produce any
+ diagnostics or set state outside of these variables. */
+
+void
+decode_cmdline_options_to_array (unsigned int argc, const char **argv,
+ unsigned int lang_mask,
+ struct cl_decoded_option **decoded_options,
+ unsigned int *decoded_options_count)
+{
+ unsigned int n, i;
+ struct cl_decoded_option *opt_array;
+ unsigned int num_decoded_options;
+
+ int opt_array_len = argc;
+ opt_array = XNEWVEC (struct cl_decoded_option, opt_array_len);
+
+ opt_array[0].opt_index = OPT_SPECIAL_program_name;
+ opt_array[0].warn_message = NULL;
+ opt_array[0].arg = argv[0];
+ opt_array[0].orig_option_with_args_text = argv[0];
+ opt_array[0].canonical_option_num_elements = 1;
+ opt_array[0].canonical_option[0] = argv[0];
+ opt_array[0].canonical_option[1] = NULL;
+ opt_array[0].canonical_option[2] = NULL;
+ opt_array[0].canonical_option[3] = NULL;
+ opt_array[0].value = 1;
+ opt_array[0].errors = 0;
+ num_decoded_options = 1;
+
+ for (i = 1; i < argc; i += n)
+ {
+ const char *opt = argv[i];
+
+ /* Interpret "-" or a non-switch as a file name. */
+ if (opt[0] != '-' || opt[1] == '\0')
+ {
+ generate_option_input_file (opt, &opt_array[num_decoded_options]);
+ num_decoded_options++;
+ n = 1;
+ continue;
+ }
+
+ /* Interpret "--param" "key=name" as "--param=key=name". */
+ const char *needle = "--param";
+ if (i + 1 < argc && strcmp (opt, needle) == 0)
+ {
+ const char *replacement
+ = opts_concat (needle, "=", argv[i + 1], NULL);
+ argv[++i] = replacement;
+ }
+
+ /* Expand -fdiagnostics-plain-output to its constituents. This needs
+ to happen here so that prune_options can handle -fdiagnostics-color
+ specially. */
+ if (!strcmp (opt, "-fdiagnostics-plain-output"))
+ {
+ /* If you have changed the default diagnostics output, and this new
+ output is not appropriately "plain" (e.g., the change needs to be
+ undone in order for the testsuite to work properly), then please do
+ the following:
+ 1. Add the necessary option to undo the new behavior to
+ the array below.
+ 2. Update the documentation for -fdiagnostics-plain-output
+ in invoke.texi. */
+ const char *const expanded_args[] = {
+ "-fno-diagnostics-show-caret",
+ "-fno-diagnostics-show-line-numbers",
+ "-fdiagnostics-color=never",
+ "-fdiagnostics-urls=never",
+ "-fdiagnostics-path-format=separate-events",
+ };
+ const int num_expanded = ARRAY_SIZE (expanded_args);
+ opt_array_len += num_expanded - 1;
+ opt_array = XRESIZEVEC (struct cl_decoded_option,
+ opt_array, opt_array_len);
+ for (int j = 0, nj; j < num_expanded; j += nj)
+ {
+ nj = decode_cmdline_option (expanded_args + j, lang_mask,
+ &opt_array[num_decoded_options]);
+ num_decoded_options++;
+ }
+
+ n = 1;
+ continue;
+ }
+
+ n = decode_cmdline_option (argv + i, lang_mask,
+ &opt_array[num_decoded_options]);
+ num_decoded_options++;
+ }
+
+ *decoded_options = opt_array;
+ *decoded_options_count = num_decoded_options;
+ prune_options (decoded_options, decoded_options_count);
+}
+
+/* Return true if NEXT_OPT_IDX cancels OPT_IDX. Return false if the
+ next one is the same as ORIG_NEXT_OPT_IDX. */
+
+static bool
+cancel_option (int opt_idx, int next_opt_idx, int orig_next_opt_idx)
+{
+ /* An option can be canceled by the same option or an option with
+ Negative. */
+ if (cl_options [next_opt_idx].neg_index == opt_idx)
+ return true;
+
+ if (cl_options [next_opt_idx].neg_index != orig_next_opt_idx)
+ return cancel_option (opt_idx, cl_options [next_opt_idx].neg_index,
+ orig_next_opt_idx);
+
+ return false;
+}
+
+/* Filter out options canceled by the ones after them. */
+
+static void
+prune_options (struct cl_decoded_option **decoded_options,
+ unsigned int *decoded_options_count)
+{
+ unsigned int old_decoded_options_count = *decoded_options_count;
+ struct cl_decoded_option *old_decoded_options = *decoded_options;
+ unsigned int new_decoded_options_count;
+ struct cl_decoded_option *new_decoded_options
+ = XNEWVEC (struct cl_decoded_option, old_decoded_options_count);
+ unsigned int i;
+ const struct cl_option *option;
+ unsigned int fdiagnostics_color_idx = 0;
+
+ /* Remove arguments which are negated by others after them. */
+ new_decoded_options_count = 0;
+ for (i = 0; i < old_decoded_options_count; i++)
+ {
+ unsigned int j, opt_idx, next_opt_idx;
+
+ if (old_decoded_options[i].errors & ~CL_ERR_WRONG_LANG)
+ goto keep;
+
+ opt_idx = old_decoded_options[i].opt_index;
+ switch (opt_idx)
+ {
+ case OPT_SPECIAL_unknown:
+ case OPT_SPECIAL_ignore:
+ case OPT_SPECIAL_warn_removed:
+ case OPT_SPECIAL_program_name:
+ case OPT_SPECIAL_input_file:
+ goto keep;
+
+ /* Do not save OPT_fdiagnostics_color_, just remember the last one. */
+ case OPT_fdiagnostics_color_:
+ fdiagnostics_color_idx = i;
+ continue;
+
+ default:
+ gcc_assert (opt_idx < cl_options_count);
+ option = &cl_options[opt_idx];
+ if (option->neg_index < 0)
+ goto keep;
+
+ /* Skip joined switches. */
+ if ((option->flags & CL_JOINED)
+ && (!option->cl_reject_negative
+ || (unsigned int) option->neg_index != opt_idx))
+ goto keep;
+
+ for (j = i + 1; j < old_decoded_options_count; j++)
+ {
+ if (old_decoded_options[j].errors & ~CL_ERR_WRONG_LANG)
+ continue;
+ next_opt_idx = old_decoded_options[j].opt_index;
+ if (next_opt_idx >= cl_options_count)
+ continue;
+ if (cl_options[next_opt_idx].neg_index < 0)
+ continue;
+ if ((cl_options[next_opt_idx].flags & CL_JOINED)
+ && (!cl_options[next_opt_idx].cl_reject_negative
+ || ((unsigned int) cl_options[next_opt_idx].neg_index
+ != next_opt_idx)))
+ continue;
+ if (cancel_option (opt_idx, next_opt_idx, next_opt_idx))
+ break;
+ }
+ if (j == old_decoded_options_count)
+ {
+keep:
+ new_decoded_options[new_decoded_options_count]
+ = old_decoded_options[i];
+ new_decoded_options_count++;
+ }
+ break;
+ }
+ }
+
+ if (fdiagnostics_color_idx >= 1)
+ {
+ /* We put the last -fdiagnostics-color= at the first position
+ after argv[0] so it can take effect immediately. */
+ memmove (new_decoded_options + 2, new_decoded_options + 1,
+ sizeof (struct cl_decoded_option)
+ * (new_decoded_options_count - 1));
+ new_decoded_options[1] = old_decoded_options[fdiagnostics_color_idx];
+ new_decoded_options_count++;
+ }
+
+ free (old_decoded_options);
+ new_decoded_options = XRESIZEVEC (struct cl_decoded_option,
+ new_decoded_options,
+ new_decoded_options_count);
+ *decoded_options = new_decoded_options;
+ *decoded_options_count = new_decoded_options_count;
+}
+
+/* Handle option DECODED for the language indicated by LANG_MASK,
+ using the handlers in HANDLERS and setting fields in OPTS and
+ OPTS_SET. KIND is the diagnostic_t if this is a diagnostics
+ option, DK_UNSPECIFIED otherwise, and LOC is the location of the
+ option for options from the source file, UNKNOWN_LOCATION
+ otherwise. GENERATED_P is true for an option generated as part of
+ processing another option or otherwise generated internally, false
+ for one explicitly passed by the user. control_warning_option
+ generated options are considered explicitly passed by the user.
+ Returns false if the switch was invalid. DC is the diagnostic
+ context for options affecting diagnostics state, or NULL. */
+
+static bool
+handle_option (struct gcc_options *opts,
+ struct gcc_options *opts_set,
+ const struct cl_decoded_option *decoded,
+ unsigned int lang_mask, int kind, location_t loc,
+ const struct cl_option_handlers *handlers,
+ bool generated_p, diagnostic_context *dc)
+{
+ size_t opt_index = decoded->opt_index;
+ const char *arg = decoded->arg;
+ HOST_WIDE_INT value = decoded->value;
+ 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);
+
+ for (i = 0; i < handlers->num_handlers; i++)
+ if (option->flags & handlers->handlers[i].mask)
+ {
+ if (!handlers->handlers[i].handler (opts, opts_set, decoded,
+ lang_mask, kind, loc,
+ handlers, dc,
+ handlers->target_option_override_hook))
+ return false;
+ }
+
+ return true;
+}
+
+/* Like handle_option, but OPT_INDEX, ARG and VALUE describe the
+ option instead of DECODED. This is used for callbacks when one
+ option implies another instead of an option being decoded from the
+ command line. */
+
+bool
+handle_generated_option (struct gcc_options *opts,
+ struct gcc_options *opts_set,
+ size_t opt_index, const char *arg, HOST_WIDE_INT value,
+ unsigned int lang_mask, int kind, location_t loc,
+ const struct cl_option_handlers *handlers,
+ bool generated_p, diagnostic_context *dc)
+{
+ struct cl_decoded_option decoded;
+
+ generate_option (opt_index, arg, value, lang_mask, &decoded);
+ return handle_option (opts, opts_set, &decoded, lang_mask, kind, loc,
+ handlers, generated_p, dc);
+}
+
+/* Fill in *DECODED with an option described by OPT_INDEX, ARG and
+ VALUE for a front end using LANG_MASK. This is used when the
+ compiler generates options internally. */
+
+void
+generate_option (size_t opt_index, const char *arg, HOST_WIDE_INT value,
+ unsigned int lang_mask, struct cl_decoded_option *decoded)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+
+ decoded->opt_index = opt_index;
+ decoded->warn_message = NULL;
+ decoded->arg = arg;
+ decoded->value = value;
+ decoded->errors = (option_ok_for_language (option, lang_mask)
+ ? 0
+ : CL_ERR_WRONG_LANG);
+
+ generate_canonical_option (opt_index, arg, value, decoded);
+ switch (decoded->canonical_option_num_elements)
+ {
+ case 1:
+ decoded->orig_option_with_args_text = decoded->canonical_option[0];
+ break;
+
+ case 2:
+ decoded->orig_option_with_args_text
+ = opts_concat (decoded->canonical_option[0], " ",
+ decoded->canonical_option[1], NULL);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Fill in *DECODED with an option for input file FILE. */
+
+void
+generate_option_input_file (const char *file,
+ struct cl_decoded_option *decoded)
+{
+ decoded->opt_index = OPT_SPECIAL_input_file;
+ decoded->warn_message = NULL;
+ decoded->arg = file;
+ decoded->orig_option_with_args_text = file;
+ decoded->canonical_option_num_elements = 1;
+ decoded->canonical_option[0] = file;
+ decoded->canonical_option[1] = NULL;
+ decoded->canonical_option[2] = NULL;
+ decoded->canonical_option[3] = NULL;
+ decoded->value = 1;
+ decoded->errors = 0;
+}
+
+/* Helper function for listing valid choices and hint for misspelled
+ value. CANDIDATES is a vector containing all valid strings,
+ STR is set to a heap allocated string that contains all those
+ strings concatenated, separated by spaces, and the return value
+ is the closest string from those to ARG, or NULL if nothing is
+ close enough. Callers should XDELETEVEC (STR) after using it
+ to avoid memory leaks. */
+
+const char *
+candidates_list_and_hint (const char *arg, char *&str,
+ const auto_vec <const char *> &candidates)
+{
+ size_t len = 0;
+ int i;
+ const char *candidate;
+ char *p;
+
+ FOR_EACH_VEC_ELT (candidates, i, candidate)
+ len += strlen (candidate) + 1;
+
+ str = p = XNEWVEC (char, len);
+ FOR_EACH_VEC_ELT (candidates, i, candidate)
+ {
+ len = strlen (candidate);
+ memcpy (p, candidate, len);
+ p[len] = ' ';
+ p += len + 1;
+ }
+ p[-1] = '\0';
+ return find_closest_string (arg, &candidates);
+}
+
+/* Perform diagnostics for read_cmdline_option and control_warning_option
+ functions. Returns true if an error has been diagnosed.
+ LOC and LANG_MASK arguments like in read_cmdline_option.
+ OPTION is the option to report diagnostics for, OPT the name
+ of the option as text, ARG the argument of the option (for joined
+ options), ERRORS is bitmask of CL_ERR_* values. */
+
+static bool
+cmdline_handle_error (location_t loc, const struct cl_option *option,
+ const char *opt, const char *arg, int errors,
+ unsigned int lang_mask)
+{
+ if (errors & CL_ERR_DISABLED)
+ {
+ error_at (loc, "command-line option %qs"
+ " is not supported by this configuration", opt);
+ return true;
+ }
+
+ if (errors & CL_ERR_MISSING_ARG)
+ {
+ if (option->missing_argument_error)
+ error_at (loc, option->missing_argument_error, opt);
+ else
+ error_at (loc, "missing argument to %qs", opt);
+ return true;
+ }
+
+ if (errors & CL_ERR_UINT_ARG)
+ {
+ if (option->cl_byte_size)
+ error_at (loc, "argument to %qs should be a non-negative integer "
+ "optionally followed by a size unit",
+ option->opt_text);
+ else
+ error_at (loc, "argument to %qs should be a non-negative integer",
+ option->opt_text);
+ return true;
+ }
+
+ if (errors & CL_ERR_INT_RANGE_ARG)
+ {
+ error_at (loc, "argument to %qs is not between %d and %d",
+ option->opt_text, option->range_min, option->range_max);
+ return true;
+ }
+
+ if (errors & CL_ERR_ENUM_ARG)
+ {
+ const struct cl_enum *e = &cl_enums[option->var_enum];
+ unsigned int i;
+ char *s;
+
+ auto_diagnostic_group d;
+ if (e->unknown_error)
+ error_at (loc, e->unknown_error, arg);
+ else
+ error_at (loc, "unrecognized argument in option %qs", opt);
+
+ auto_vec <const char *> candidates;
+ for (i = 0; e->values[i].arg != NULL; i++)
+ {
+ if (!enum_arg_ok_for_language (&e->values[i], lang_mask))
+ continue;
+ candidates.safe_push (e->values[i].arg);
+ }
+ const char *hint = candidates_list_and_hint (arg, s, candidates);
+ if (hint)
+ inform (loc, "valid arguments to %qs are: %s; did you mean %qs?",
+ option->opt_text, s, hint);
+ else
+ inform (loc, "valid arguments to %qs are: %s", option->opt_text, s);
+ XDELETEVEC (s);
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Handle the switch DECODED (location LOC) for the language indicated
+ by LANG_MASK, using the handlers in *HANDLERS and setting fields in
+ OPTS and OPTS_SET and using diagnostic context DC (if not NULL) for
+ diagnostic options. */
+
+void
+read_cmdline_option (struct gcc_options *opts,
+ struct gcc_options *opts_set,
+ struct cl_decoded_option *decoded,
+ location_t loc,
+ unsigned int lang_mask,
+ const struct cl_option_handlers *handlers,
+ diagnostic_context *dc)
+{
+ const struct cl_option *option;
+ const char *opt = decoded->orig_option_with_args_text;
+
+ if (decoded->warn_message)
+ warning_at (loc, 0, decoded->warn_message, opt);
+
+ if (decoded->opt_index == OPT_SPECIAL_unknown)
+ {
+ if (handlers->unknown_option_callback (decoded))
+ error_at (loc, "unrecognized command-line option %qs", decoded->arg);
+ return;
+ }
+
+ if (decoded->opt_index == OPT_SPECIAL_ignore)
+ return;
+
+ if (decoded->opt_index == OPT_SPECIAL_warn_removed)
+ {
+ /* Warn only about positive ignored options. */
+ if (decoded->value)
+ warning_at (loc, 0, "switch %qs is no longer supported", opt);
+ return;
+ }
+
+ option = &cl_options[decoded->opt_index];
+
+ if (decoded->errors
+ && cmdline_handle_error (loc, option, opt, decoded->arg,
+ decoded->errors, lang_mask))
+ return;
+
+ if (decoded->errors & CL_ERR_WRONG_LANG)
+ {
+ handlers->wrong_lang_callback (decoded, lang_mask);
+ return;
+ }
+
+ gcc_assert (!decoded->errors);
+
+ if (!handle_option (opts, opts_set, decoded, lang_mask, DK_UNSPECIFIED,
+ loc, handlers, false, dc))
+ error_at (loc, "unrecognized command-line option %qs", opt);
+}
+
+/* Set any field in OPTS, and OPTS_SET if not NULL, for option
+ OPT_INDEX according to VALUE and ARG, diagnostic kind KIND,
+ location LOC, using diagnostic context DC if not NULL for
+ diagnostic classification. */
+
+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)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+ void *flag_var = option_flag_var (opt_index, opts);
+ void *set_flag_var = NULL;
+
+ if (!flag_var)
+ return;
+
+ if ((diagnostic_t) kind != DK_UNSPECIFIED && dc != NULL)
+ diagnostic_classify_diagnostic (dc, opt_index, (diagnostic_t) kind, loc);
+
+ if (opts_set != NULL)
+ set_flag_var = option_flag_var (opt_index, opts_set);
+
+ switch (option->var_type)
+ {
+ case CLVC_INTEGER:
+ if (option->cl_host_wide_int)
+ {
+ *(HOST_WIDE_INT *) flag_var = value;
+ if (set_flag_var)
+ *(HOST_WIDE_INT *) set_flag_var = 1;
+ }
+ else
+ {
+ if (value > INT_MAX)
+ error_at (loc, "argument to %qs is bigger than %d",
+ option->opt_text, INT_MAX);
+ else
+ {
+ *(int *) flag_var = value;
+ if (set_flag_var)
+ *(int *) set_flag_var = 1;
+ }
+ }
+
+ break;
+
+ case CLVC_SIZE:
+ if (option->cl_host_wide_int)
+ {
+ *(HOST_WIDE_INT *) flag_var = value;
+ if (set_flag_var)
+ *(HOST_WIDE_INT *) set_flag_var = value;
+ }
+ else
+ {
+ *(int *) flag_var = value;
+ if (set_flag_var)
+ *(int *) set_flag_var = value;
+ }
+
+ break;
+
+ case CLVC_EQUAL:
+ if (option->cl_host_wide_int)
+ {
+ *(HOST_WIDE_INT *) flag_var = (value
+ ? option->var_value
+ : !option->var_value);
+ if (set_flag_var)
+ *(HOST_WIDE_INT *) set_flag_var = 1;
+ }
+ else
+ {
+ *(int *) flag_var = (value
+ ? option->var_value
+ : !option->var_value);
+ if (set_flag_var)
+ *(int *) set_flag_var = 1;
+ }
+ break;
+
+ case CLVC_BIT_CLEAR:
+ case CLVC_BIT_SET:
+ if ((value != 0) == (option->var_type == CLVC_BIT_SET))
+ {
+ if (option->cl_host_wide_int)
+ *(HOST_WIDE_INT *) flag_var |= option->var_value;
+ else
+ *(int *) flag_var |= option->var_value;
+ }
+ else
+ {
+ if (option->cl_host_wide_int)
+ *(HOST_WIDE_INT *) flag_var &= ~option->var_value;
+ else
+ *(int *) flag_var &= ~option->var_value;
+ }
+ if (set_flag_var)
+ {
+ if (option->cl_host_wide_int)
+ *(HOST_WIDE_INT *) set_flag_var |= option->var_value;
+ else
+ *(int *) set_flag_var |= option->var_value;
+ }
+ break;
+
+ case CLVC_STRING:
+ *(const char **) flag_var = arg;
+ if (set_flag_var)
+ *(const char **) set_flag_var = "";
+ break;
+
+ case CLVC_ENUM:
+ {
+ const struct cl_enum *e = &cl_enums[option->var_enum];
+
+ e->set (flag_var, value);
+ if (set_flag_var)
+ e->set (set_flag_var, 1);
+ }
+ break;
+
+ case CLVC_DEFER:
+ {
+ vec<cl_deferred_option> *v
+ = (vec<cl_deferred_option> *) *(void **) flag_var;
+ cl_deferred_option p = {opt_index, arg, value};
+ if (!v)
+ v = XCNEW (vec<cl_deferred_option>);
+ v->safe_push (p);
+ *(void **) flag_var = v;
+ if (set_flag_var)
+ *(void **) set_flag_var = v;
+ }
+ break;
+ }
+}
+
+/* Return the address of the flag variable for option OPT_INDEX in
+ options structure OPTS, or NULL if there is no flag variable. */
+
+void *
+option_flag_var (int opt_index, struct gcc_options *opts)
+{
+ const struct cl_option *option = &cl_options[opt_index];
+
+ if (option->flag_var_offset == (unsigned short) -1)
+ return NULL;
+ return (void *)(((char *) opts) + option->flag_var_offset);
+}
+
+/* Return 1 if option OPT_IDX is enabled in OPTS, 0 if it is disabled,
+ or -1 if it isn't a simple on-off switch
+ (or if the value is unknown, typically set later in target). */
+
+int
+option_enabled (int opt_idx, unsigned lang_mask, void *opts)
+{
+ const struct cl_option *option = &(cl_options[opt_idx]);
+
+ /* A language-specific option can only be considered enabled when it's
+ valid for the current language. */
+ if (!(option->flags & CL_COMMON)
+ && (option->flags & CL_LANG_ALL)
+ && !(option->flags & lang_mask))
+ return 0;
+
+ struct gcc_options *optsg = (struct gcc_options *) opts;
+ void *flag_var = option_flag_var (opt_idx, optsg);
+
+ if (flag_var)
+ switch (option->var_type)
+ {
+ case CLVC_INTEGER:
+ if (option->cl_host_wide_int)
+ {
+ HOST_WIDE_INT v = *(HOST_WIDE_INT *) flag_var;
+ return v != 0 ? (v < 0 ? -1 : 1) : 0;
+ }
+ else
+ {
+ int v = *(int *) flag_var;
+ return v != 0 ? (v < 0 ? -1 : 1) : 0;
+ }
+
+ case CLVC_EQUAL:
+ if (option->cl_host_wide_int)
+ return *(HOST_WIDE_INT *) flag_var == option->var_value;
+ else
+ return *(int *) flag_var == option->var_value;
+
+ case CLVC_BIT_CLEAR:
+ if (option->cl_host_wide_int)
+ return (*(HOST_WIDE_INT *) flag_var & option->var_value) == 0;
+ else
+ return (*(int *) flag_var & option->var_value) == 0;
+
+ case CLVC_BIT_SET:
+ if (option->cl_host_wide_int)
+ return (*(HOST_WIDE_INT *) flag_var & option->var_value) != 0;
+ else
+ return (*(int *) flag_var & option->var_value) != 0;
+
+ case CLVC_SIZE:
+ if (option->cl_host_wide_int)
+ return *(HOST_WIDE_INT *) flag_var != -1;
+ else
+ return *(int *) flag_var != -1;
+
+ case CLVC_STRING:
+ case CLVC_ENUM:
+ case CLVC_DEFER:
+ break;
+ }
+ return -1;
+}
+
+/* Fill STATE with the current state of option OPTION in OPTS. Return
+ true if there is some state to store. */
+
+bool
+get_option_state (struct gcc_options *opts, int option,
+ struct cl_option_state *state)
+{
+ void *flag_var = option_flag_var (option, opts);
+
+ if (flag_var == 0)
+ return false;
+
+ switch (cl_options[option].var_type)
+ {
+ case CLVC_INTEGER:
+ case CLVC_EQUAL:
+ case CLVC_SIZE:
+ state->data = flag_var;
+ state->size = (cl_options[option].cl_host_wide_int
+ ? sizeof (HOST_WIDE_INT)
+ : sizeof (int));
+ break;
+
+ case CLVC_BIT_CLEAR:
+ case CLVC_BIT_SET:
+ state->ch = option_enabled (option, -1, opts);
+ state->data = &state->ch;
+ state->size = 1;
+ break;
+
+ case CLVC_STRING:
+ state->data = *(const char **) flag_var;
+ if (state->data == 0)
+ state->data = "";
+ state->size = strlen ((const char *) state->data) + 1;
+ break;
+
+ case CLVC_ENUM:
+ state->data = flag_var;
+ state->size = cl_enums[cl_options[option].var_enum].var_size;
+ break;
+
+ case CLVC_DEFER:
+ return false;
+ }
+ return true;
+}
+
+/* Set a warning option OPT_INDEX (language mask LANG_MASK, option
+ handlers HANDLERS) to have diagnostic kind KIND for option
+ structures OPTS and OPTS_SET and diagnostic context DC (possibly
+ NULL), at location LOC (UNKNOWN_LOCATION for -Werror=). ARG is the
+ argument of the option for joined options, or NULL otherwise. If IMPLY,
+ the warning option in question is implied at this point. This is
+ used by -Werror= and #pragma GCC diagnostic. */
+
+void
+control_warning_option (unsigned int opt_index, int kind, const char *arg,
+ bool imply, location_t loc, unsigned int lang_mask,
+ const struct cl_option_handlers *handlers,
+ struct gcc_options *opts,
+ struct gcc_options *opts_set,
+ diagnostic_context *dc)
+{
+ if (cl_options[opt_index].alias_target != N_OPTS)
+ {
+ gcc_assert (!cl_options[opt_index].cl_separate_alias
+ && !cl_options[opt_index].cl_negative_alias);
+ if (cl_options[opt_index].alias_arg)
+ arg = cl_options[opt_index].alias_arg;
+ opt_index = cl_options[opt_index].alias_target;
+ }
+ if (opt_index == OPT_SPECIAL_ignore || opt_index == OPT_SPECIAL_warn_removed)
+ return;
+ if (dc)
+ diagnostic_classify_diagnostic (dc, opt_index, (diagnostic_t) kind, loc);
+ if (imply)
+ {
+ const struct cl_option *option = &cl_options[opt_index];
+
+ /* -Werror=foo implies -Wfoo. */
+ if (option->var_type == CLVC_INTEGER
+ || option->var_type == CLVC_ENUM
+ || option->var_type == CLVC_SIZE)
+ {
+ HOST_WIDE_INT value = 1;
+
+ if (arg && *arg == '\0' && !option->cl_missing_ok)
+ arg = NULL;
+
+ if ((option->flags & CL_JOINED) && arg == NULL)
+ {
+ cmdline_handle_error (loc, option, option->opt_text, arg,
+ CL_ERR_MISSING_ARG, lang_mask);
+ return;
+ }
+
+ /* If the switch takes an integer argument, convert it. */
+ if (arg && (option->cl_uinteger || option->cl_host_wide_int))
+ {
+ int error = 0;
+ value = *arg ? integral_argument (arg, &error,
+ option->cl_byte_size) : 0;
+ if (error)
+ {
+ cmdline_handle_error (loc, option, option->opt_text, arg,
+ CL_ERR_UINT_ARG, lang_mask);
+ return;
+ }
+ }
+
+ /* If the switch takes an enumerated argument, convert it. */
+ if (arg && option->var_type == CLVC_ENUM)
+ {
+ const struct cl_enum *e = &cl_enums[option->var_enum];
+
+ if (enum_arg_to_value (e->values, arg, &value, lang_mask))
+ {
+ const char *carg = NULL;
+
+ if (enum_value_to_arg (e->values, &carg, value, lang_mask))
+ arg = carg;
+ gcc_assert (carg != NULL);
+ }
+ else
+ {
+ cmdline_handle_error (loc, option, option->opt_text, arg,
+ CL_ERR_ENUM_ARG, lang_mask);
+ return;
+ }
+ }
+
+ handle_generated_option (opts, opts_set,
+ opt_index, arg, value, lang_mask,
+ kind, loc, handlers, false, dc);
+ }
+ }
+}
+
+/* Parse options in COLLECT_GCC_OPTIONS and push them on ARGV_OBSTACK.
+ Store number of arguments into ARGC_P. */
+
+void
+parse_options_from_collect_gcc_options (const char *collect_gcc_options,
+ obstack *argv_obstack,
+ int *argc_p)
+{
+ char *argv_storage = xstrdup (collect_gcc_options);
+ int j, k;
+
+ for (j = 0, k = 0; argv_storage[j] != '\0'; ++j)
+ {
+ if (argv_storage[j] == '\'')
+ {
+ obstack_ptr_grow (argv_obstack, &argv_storage[k]);
+ ++j;
+ do
+ {
+ if (argv_storage[j] == '\0')
+ fatal_error (input_location,
+ "malformed %<COLLECT_GCC_OPTIONS%>");
+ else if (startswith (&argv_storage[j], "'\\''"))
+ {
+ argv_storage[k++] = '\'';
+ j += 4;
+ }
+ else if (argv_storage[j] == '\'')
+ break;
+ else
+ argv_storage[k++] = argv_storage[j++];
+ }
+ while (1);
+ argv_storage[k++] = '\0';
+ }
+ }
+
+ obstack_ptr_grow (argv_obstack, NULL);
+ *argc_p = obstack_object_size (argv_obstack) / sizeof (void *) - 1;
+}
+
+/* Prepend -Xassembler for each option in COLLECT_AS_OPTIONS,
+ and push on O. */
+
+void prepend_xassembler_to_collect_as_options (const char *collect_as_options,
+ obstack *o)
+{
+ obstack opts_obstack;
+ int opts_count;
+
+ obstack_init (&opts_obstack);
+ parse_options_from_collect_gcc_options (collect_as_options,
+ &opts_obstack, &opts_count);
+ const char **assembler_opts = XOBFINISH (&opts_obstack, const char **);
+
+ for (int i = 0; i < opts_count; i++)
+ {
+ obstack_grow (o, " '-Xassembler' ",
+ strlen (" '-Xassembler' "));
+ const char *opt = assembler_opts[i];
+ obstack_1grow (o, '\'');
+ obstack_grow (o, opt, strlen (opt));
+ obstack_1grow (o, '\'');
+ }
+}