Patchwork Convert options to array of cl_decoded_option structures

login
register
mail settings
Submitter Joseph S. Myers
Date June 18, 2010, 10:16 a.m.
Message ID <Pine.LNX.4.64.1006181014470.28040@digraph.polyomino.org.uk>
Download mbox | patch
Permalink /patch/56164/
State New
Headers show

Comments

Joseph S. Myers - June 18, 2010, 10:16 a.m.
This patch continues work towards sharing option-processing code
between the driver and the compilers proper by creating a function in
opts-common.c that deals with converting the argv array into an array
of cl_decoded_option structures.

The prescan for -O options and the handling of -frecord-gcc-switches
are converted to use this array.  In both cases, this simplifies the
code in question and fixes bugs in it: for -O options, bugs relating
to option arguments that look like -O options; for
-frecord-gcc-switches, the issue where it wrongly identifies what is
an option argument (one of many issues mentioned in PR 32998; the
others are not addressed by this patch).  The function
read_integral_parameter, used only for -O options, essentially
duplicated integral_argument, used for other options, and is removed
by this patch.  Front-end option handlers will never get called with
non-options, so there is no need for the Fortran handler to consider
that possibility, and I removed that code as part of eliminating some
special uses of N_OPTS/cl_options_count in favour of
OPT_SPECIAL_unknown.

Note that the front-end init_options hooks still take argc and argv,
and the c-family hook uses them for one of the various scans that
don't handle option arguments properly (to scan for -lang-asm).  My
inclination is that the special code for -lang-asm should be replaced
with a langhook that determines whether to warn for a wrong-language
option, while init_options is split into one part that determines the
lang_mask used for parsing options (without receiving the options
itself) and another part that receives the decoded options.  A hook
determining whether to warn for wrong-language options would also
replace the hardcoding of an LTO check in complain_wrong_lang.  When
the option processing code is used in the driver, it would simply
quietly accept options valid for any language.

Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  OK to
commit?

2010-06-18  Joseph Myers  <joseph@codesourcery.com>

	PR other/32998
	* opth-gen.awk: Generate definitions of OPT_SPECIAL_unknown,
	OPT_SPECIAL_program_name and OPT_SPECIAL_input_file.
	* opts-common.c (find_opt): Return OPT_SPECIAL_unknown on failure.
	(decode_cmdline_option): Update for this return value.  Set
	orig_option_with_args_text field.  Set arg field for unknown
	options.  Make static.
	(decode_cmdline_options_to_array): New.
	(prune_options): Update handling of find_opt return value.
	* opts.c (read_cmdline_option): Take decoded option.  Return void.
	(read_cmdline_options): Take decoded options.
	(decode_options): Add parameters for decoded options.  Use
	decode_cmdline_options_to_array.  Use decoded options for -O
	scan.  Use integral_argument for -O parameters.  Update call to
	read_cmdline_options.
	(enable_warning_as_error): Update handling of find_opt return
	value.
	* opts.h: Update comment on unknown options.
	(struct cl_decoded_option): Update comments on opt_index and arg.
	Add orig_option_with_args_text.
	(decode_cmdline_option): Remove.
	(decode_cmdline_options_to_array): Declare.
	(decode_options): Update prototype.
	* toplev.c (save_argv): Remove.
	(save_decoded_options, save_decoded_options_count): New.
	(read_integral_parameter): Remove.
	(print_switch_values): Use decoded options.
	(toplev_main): Don't set save_argv.  Update call to
	decode_options.
	* toplev.h (read_integral_parameter): Remove.
	* varasm.c (elf_record_gcc_switches): Don't handle holding back
	names.

c-family:
2010-06-18  Joseph Myers  <joseph@codesourcery.com>

	* c-common.c (parse_optimize_options): Update call to
	decode_options.

fortran:
2010-06-18  Joseph Myers  <joseph@codesourcery.com>

	* options.c (gfc_handle_option): Don't handle N_OPTS.

testsuite:
2010-06-18  Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/opts-2.c: New test.
Manuel López-Ibáñez - June 18, 2010, 10:45 a.m.
On 18 June 2010 12:16, Joseph S. Myers <joseph@codesourcery.com> wrote:
> +       case OPT_Ofast:
> +         /* -Ofast only adds flags to -O3.  */
> +         optimize_size = 0;
> +         optimize = 3;
> +         ofast = 1;
> +         break;

I believe that the original idea was that commandline options should
enable other options by simulating that they appeared in the command
line. That is the purpose of the new handle_option and how it is done
for Wimplicit. Has the plan changed now? Or are you planning to do
this in a follow-up patch?

Manuel.
Joseph S. Myers - June 18, 2010, 11:13 a.m.
On Fri, 18 Jun 2010, Manuel López-Ibáñez wrote:

> On 18 June 2010 12:16, Joseph S. Myers <joseph@codesourcery.com> wrote:
> > +       case OPT_Ofast:
> > +         /* -Ofast only adds flags to -O3.  */
> > +         optimize_size = 0;
> > +         optimize = 3;
> > +         ofast = 1;
> > +         break;
> 
> I believe that the original idea was that commandline options should
> enable other options by simulating that they appeared in the command
> line. That is the purpose of the new handle_option and how it is done
> for Wimplicit. Has the plan changed now? Or are you planning to do
> this in a follow-up patch?

I don't understand how this comment relates to my patch; it appears to be 
about something my patch isn't changing or aiming to change.  This patch 
is causing all code in the compilers proper after a certain point that 
previously worked with the argv array to work with decoded options instead 
- and the particular part you quote is converting the -O prescan to do 
just that, without doing anything else about that prescan - the logic 
handling each option in the prescan is unchanged except that it no longer 
decodes strings from argv itself.  The purpose of this patch is not to 
implement any general infrastructure for options implying other options 
(although I have described what I think the semantics should be for this, 
it may not be necessary to implement that to achieve the desired effects 
of this patch series).  In principle, I do indeed think that the prescan 
should not be needed - a general enough infrastructure to describe what 
other options the -O options imply should be able to replace it.  But 
sharing the existing code for -O prescans with the driver is surely going 
to be substantially simpler than a sufficiently general implication 
infrastructure.

I should point out that I don't really think of -Ofast as implying -O3 in 
the normal sense of option implications.  Rather, I think of -O as more 
like an option with enumerated arguments (1, 2, 3, s, fast), where no 
argument is an alias for 1 and arguments that are integers greater than 3 
are aliases for 3; logically, you'd take the value of that enumeration 
from the last -O option present, and only then convert that value into the 
settings of multiple variables.  I *do* expect to implement some generic 
support for options with enumerated arguments in this patch series - there 
are so many instances of ad hoc code for such options at present - but it 
probably won't cover the special case of -O, just the more normal cases 
such as -march where there is a clear limited finite set of arguments.

The anticipated next patches in this series are, roughly (and not 
necessarily in this exact order):

* Split up init_options langhook into part returning lang_mask and part 
that takes decoded options, together with a hook to say whether to warn 
for wrong-language options.

* Remove unused C-family -lang-objc option.

* Convert missing_argument langhook into a message string given in the 
.opt file.

* Move read_cmdline_option and handle_option to common code - taking an 
array of structures describing the handlers to call, so that the basic 
infrastructure can be shared with the driver before the handlers 
themselves are.

* Start actually using the opts-common.c infrastructure for parsing 
options in the driver (this may well need further preliminary patches).

I do not have detailed plans after that point, but general themes include 
introducing more infrastructure for options to be described more fully in 
the .opt files instead of in code so that hooks shared with the driver end 
up smaller (a system for one option to be an exact alias of another is 
definitely a part of this, as I expect is handling for enumerated 
options), and moving the existing options global variables into a 
structure to which various options functions take pointers (this last 
piece is critical to the whole purpose of this patch series, but all the 
other pieces are involved in ensuring that the structure contents are 
accurate enough in the driver to be used in multilib selection).
Manuel López-Ibáñez - June 18, 2010, 12:12 p.m.
On 18 June 2010 13:13, Joseph S. Myers <joseph@codesourcery.com> wrote:
> On Fri, 18 Jun 2010, Manuel López-Ibáñez wrote:
>
>> On 18 June 2010 12:16, Joseph S. Myers <joseph@codesourcery.com> wrote:
>> > +       case OPT_Ofast:
>> > +         /* -Ofast only adds flags to -O3.  */
>> > +         optimize_size = 0;
>> > +         optimize = 3;
>> > +         ofast = 1;
>> > +         break;
>>
>> I believe that the original idea was that commandline options should
>> enable other options by simulating that they appeared in the command
>> line. That is the purpose of the new handle_option and how it is done
>> for Wimplicit. Has the plan changed now? Or are you planning to do
>> this in a follow-up patch?

> I should point out that I don't really think of -Ofast as implying -O3 in
> the normal sense of option implications.  Rather, I think of -O as more
> like an option with enumerated arguments (1, 2, 3, s, fast), where no
> argument is an alias for 1 and arguments that are integers greater than 3
> are aliases for 3; logically, you'd take the value of that enumeration
> from the last -O option present, and only then convert that value into the
> settings of multiple variables.  I *do* expect to implement some generic
> support for options with enumerated arguments in this patch series - there
> are so many instances of ad hoc code for such options at present - but it
> probably won't cover the special case of -O, just the more normal cases
> such as -march where there is a clear limited finite set of arguments.

OK, I misunderstood that cleaning up the options that enable other
options was part of your goals. In general, for options implying other
options (which seems is not the case of -Ofast and I misunderstood
your patch), should we start using handle_option or only when that
fixes a reported bug like in the Wimplicit case?

Manuel.
Joseph S. Myers - June 18, 2010, 12:59 p.m.
On Fri, 18 Jun 2010, Manuel López-Ibáñez wrote:

> OK, I misunderstood that cleaning up the options that enable other
> options was part of your goals. In general, for options implying other

The goal is selecting multilibs based on the features enabled by the 
command line (and the features enabled by the command lines for each 
available multilib) rather than based on the text of the options in that 
command line.  Cleanups are done where they seem helpful (not always 
strictly required) for this goal - so increasing the proportion of option 
handling done by generic machinery rather than by hooks is helpful insofar 
as it reduces the amount of hook code that will end up needing to be made 
to work in two places (but front-end options are of less concern here than 
back-end options because front-end options should not generally be 
relevant to multilib selection).  Where existing bugs show up that seem 
too far afield to fix as part of this work, I expect to file PRs for them 
(e.g. PR 44574).

> options (which seems is not the case of -Ofast and I misunderstood
> your patch), should we start using handle_option or only when that
> fixes a reported bug like in the Wimplicit case?

I do not have specific advice here.  Fixing bugs (adding testcases in the 
process) seems appropriate whether or not those bugs have PRs filed in 
Bugzilla.
Richard Guenther - June 20, 2010, 8:53 p.m.
On Fri, Jun 18, 2010 at 12:16 PM, Joseph S. Myers
<joseph@codesourcery.com> wrote:
> This patch continues work towards sharing option-processing code
> between the driver and the compilers proper by creating a function in
> opts-common.c that deals with converting the argv array into an array
> of cl_decoded_option structures.
>
> The prescan for -O options and the handling of -frecord-gcc-switches
> are converted to use this array.  In both cases, this simplifies the
> code in question and fixes bugs in it: for -O options, bugs relating
> to option arguments that look like -O options; for
> -frecord-gcc-switches, the issue where it wrongly identifies what is
> an option argument (one of many issues mentioned in PR 32998; the
> others are not addressed by this patch).  The function
> read_integral_parameter, used only for -O options, essentially
> duplicated integral_argument, used for other options, and is removed
> by this patch.  Front-end option handlers will never get called with
> non-options, so there is no need for the Fortran handler to consider
> that possibility, and I removed that code as part of eliminating some
> special uses of N_OPTS/cl_options_count in favour of
> OPT_SPECIAL_unknown.
>
> Note that the front-end init_options hooks still take argc and argv,
> and the c-family hook uses them for one of the various scans that
> don't handle option arguments properly (to scan for -lang-asm).  My
> inclination is that the special code for -lang-asm should be replaced
> with a langhook that determines whether to warn for a wrong-language
> option, while init_options is split into one part that determines the
> lang_mask used for parsing options (without receiving the options
> itself) and another part that receives the decoded options.  A hook
> determining whether to warn for wrong-language options would also
> replace the hardcoding of an LTO check in complain_wrong_lang.  When
> the option processing code is used in the driver, it would simply
> quietly accept options valid for any language.
>
> Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  OK to
> commit?

Ok.

Thanks,
Richard.

> 2010-06-18  Joseph Myers  <joseph@codesourcery.com>
>
>        PR other/32998
>        * opth-gen.awk: Generate definitions of OPT_SPECIAL_unknown,
>        OPT_SPECIAL_program_name and OPT_SPECIAL_input_file.
>        * opts-common.c (find_opt): Return OPT_SPECIAL_unknown on failure.
>        (decode_cmdline_option): Update for this return value.  Set
>        orig_option_with_args_text field.  Set arg field for unknown
>        options.  Make static.
>        (decode_cmdline_options_to_array): New.
>        (prune_options): Update handling of find_opt return value.
>        * opts.c (read_cmdline_option): Take decoded option.  Return void.
>        (read_cmdline_options): Take decoded options.
>        (decode_options): Add parameters for decoded options.  Use
>        decode_cmdline_options_to_array.  Use decoded options for -O
>        scan.  Use integral_argument for -O parameters.  Update call to
>        read_cmdline_options.
>        (enable_warning_as_error): Update handling of find_opt return
>        value.
>        * opts.h: Update comment on unknown options.
>        (struct cl_decoded_option): Update comments on opt_index and arg.
>        Add orig_option_with_args_text.
>        (decode_cmdline_option): Remove.
>        (decode_cmdline_options_to_array): Declare.
>        (decode_options): Update prototype.
>        * toplev.c (save_argv): Remove.
>        (save_decoded_options, save_decoded_options_count): New.
>        (read_integral_parameter): Remove.
>        (print_switch_values): Use decoded options.
>        (toplev_main): Don't set save_argv.  Update call to
>        decode_options.
>        * toplev.h (read_integral_parameter): Remove.
>        * varasm.c (elf_record_gcc_switches): Don't handle holding back
>        names.
>
> c-family:
> 2010-06-18  Joseph Myers  <joseph@codesourcery.com>
>
>        * c-common.c (parse_optimize_options): Update call to
>        decode_options.
>
> fortran:
> 2010-06-18  Joseph Myers  <joseph@codesourcery.com>
>
>        * options.c (gfc_handle_option): Don't handle N_OPTS.
>
> testsuite:
> 2010-06-18  Joseph Myers  <joseph@codesourcery.com>
>
>        * gcc.dg/opts-2.c: New test.
>
> Index: opts-common.c
> ===================================================================
> --- opts-common.c       (revision 160895)
> +++ opts-common.c       (working copy)
> @@ -25,8 +25,8 @@ along with GCC; see the file COPYING3.
>  #include "options.h"
>
>  /* Perform a binary search to find which option the command-line INPUT
> -   matches.  Returns its index in the option array, and N_OPTS
> -   (cl_options_count) on failure.
> +   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
> @@ -73,8 +73,8 @@ find_opt (const char *input, int lang_ma
>     }
>
>   /* This is the switch that is the best match but for a different
> -     front end, or cl_options_count if there is no match at all.  */
> -  match_wrong_lang = cl_options_count;
> +     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
> @@ -94,7 +94,7 @@ find_opt (const char *input, int lang_ma
>
>          /* If we haven't remembered a prior match, remember this
>             one.  Any prior match is necessarily better.  */
> -         if (match_wrong_lang == cl_options_count)
> +         if (match_wrong_lang == OPT_SPECIAL_unknown)
>            match_wrong_lang = mn;
>        }
>
> @@ -104,7 +104,7 @@ find_opt (const char *input, int lang_ma
>     }
>   while (mn != cl_options_count);
>
> -  /* Return the best wrong match, or cl_options_count if none.  */
> +  /* Return the best wrong match, or OPT_SPECIAL_unknown if none.  */
>   return match_wrong_lang;
>  }
>
> @@ -129,7 +129,7 @@ integral_argument (const char *arg)
>    LANG_MASK, into the structure *DECODED.  Returns the number of
>    switches consumed.  */
>
> -unsigned int
> +static unsigned int
>  decode_cmdline_option (const char **argv, unsigned int lang_mask,
>                       struct cl_decoded_option *decoded)
>  {
> @@ -144,7 +144,7 @@ decode_cmdline_option (const char **argv
>   opt = argv[0];
>
>   opt_index = find_opt (opt + 1, lang_mask | CL_COMMON | CL_TARGET);
> -  if (opt_index == cl_options_count
> +  if (opt_index == OPT_SPECIAL_unknown
>       && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
>       && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
>     {
> @@ -160,8 +160,11 @@ decode_cmdline_option (const char **argv
>       opt_index = find_opt (opt + 1, lang_mask | CL_COMMON | CL_TARGET);
>     }
>
> -  if (opt_index == cl_options_count)
> -    goto done;
> +  if (opt_index == OPT_SPECIAL_unknown)
> +    {
> +      arg = argv[0];
> +      goto done;
> +    }
>
>   option = &cl_options[opt_index];
>
> @@ -169,7 +172,8 @@ decode_cmdline_option (const char **argv
>      unrecognized.  */
>   if (!value && (option->flags & CL_REJECT_NEGATIVE))
>     {
> -      opt_index = cl_options_count;
> +      opt_index = OPT_SPECIAL_unknown;
> +      arg = argv[0];
>       goto done;
>     }
>
> @@ -237,9 +241,76 @@ decode_cmdline_option (const char **argv
>   decoded->arg = arg;
>   decoded->value = value;
>   decoded->errors = errors;
> +  switch (result)
> +    {
> +    case 1:
> +      decoded->orig_option_with_args_text = argv[0];
> +      break;
> +    case 2:
> +      decoded->orig_option_with_args_text = concat (argv[0], " ",
> +                                                   argv[1], NULL);
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
>   return result;
>  }
>
> +/* 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
> +   applicable for decoding.  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;
> +
> +  opt_array = XNEWVEC (struct cl_decoded_option, argc);
> +
> +  opt_array[0].opt_index = OPT_SPECIAL_program_name;
> +  opt_array[0].arg = argv[0];
> +  opt_array[0].orig_option_with_args_text = argv[0];
> +  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')
> +       {
> +         opt_array[num_decoded_options].opt_index = OPT_SPECIAL_input_file;
> +         opt_array[num_decoded_options].arg = opt;
> +         opt_array[num_decoded_options].orig_option_with_args_text = opt;
> +         opt_array[num_decoded_options].value = 1;
> +         opt_array[num_decoded_options].errors = 0;
> +         num_decoded_options++;
> +         n = 1;
> +         continue;
> +       }
> +
> +      n = decode_cmdline_option (argv + i, lang_mask,
> +                                &opt_array[num_decoded_options]);
> +      num_decoded_options++;
> +    }
> +
> +  opt_array = XRESIZEVEC (struct cl_decoded_option, opt_array,
> +                         num_decoded_options);
> +  *decoded_options = opt_array;
> +  *decoded_options_count = num_decoded_options;
> +}
> +
>  /* Return true if NEXT_OPT_IDX cancels OPT_IDX.  Return false if the
>    next one is the same as ORIG_NEXT_OPT_IDX.  */
>
> @@ -281,7 +352,7 @@ prune_options (int *argcp, char ***argvp
>       const char *opt = (*argvp) [i];
>
>       opt_index = find_opt (opt + 1, -1);
> -      if (opt_index == cl_options_count
> +      if (opt_index == OPT_SPECIAL_unknown
>          && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
>          && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
>        {
> @@ -300,7 +371,7 @@ prune_options (int *argcp, char ***argvp
>          free (dup);
>        }
>
> -      if (opt_index == cl_options_count)
> +      if (opt_index == OPT_SPECIAL_unknown)
>        {
>  cont:
>          options [i] = 0;
> Index: c-family/c-common.c
> ===================================================================
> --- c-family/c-common.c (revision 160895)
> +++ c-family/c-common.c (working copy)
> @@ -7624,6 +7624,8 @@ parse_optimize_options (tree args, bool
>   unsigned i;
>   int saved_flag_strict_aliasing;
>   const char **opt_argv;
> +  struct cl_decoded_option *decoded_options;
> +  unsigned int decoded_options_count;
>   tree ap;
>
>   /* Build up argv vector.  Just in case the string is stored away, use garbage
> @@ -7716,7 +7718,8 @@ parse_optimize_options (tree args, bool
>   saved_flag_strict_aliasing = flag_strict_aliasing;
>
>   /* Now parse the options.  */
> -  decode_options (opt_argc, opt_argv);
> +  decode_options (opt_argc, opt_argv, &decoded_options,
> +                 &decoded_options_count);
>
>   targetm.override_options_after_change();
>
> Index: toplev.c
> ===================================================================
> --- toplev.c    (revision 160895)
> +++ toplev.c    (working copy)
> @@ -126,8 +126,9 @@ static bool no_backend;
>  /* Length of line when printing switch values.  */
>  #define MAX_LINE 75
>
> -/* Copy of argument vector to toplev_main.  */
> -static const char **save_argv;
> +/* Decoded options, and number of such options.  */
> +static struct cl_decoded_option *save_decoded_options;
> +static unsigned int save_decoded_options_count;
>
>  /* Name of top-level original source file (what was input to cpp).
>    This comes from the #-command at the beginning of the actual input.
> @@ -488,34 +489,6 @@ set_random_seed (const char *val)
>   return old;
>  }
>
> -/* Decode the string P as an integral parameter.
> -   If the string is indeed an integer return its numeric value else
> -   issue an Invalid Option error for the option PNAME and return DEFVAL.
> -   If PNAME is zero just return DEFVAL, do not call error.  */
> -
> -int
> -read_integral_parameter (const char *p, const char *pname, const int  defval)
> -{
> -  const char *endp = p;
> -
> -  while (*endp)
> -    {
> -      if (ISDIGIT (*endp))
> -       endp++;
> -      else
> -       break;
> -    }
> -
> -  if (*endp != 0)
> -    {
> -      if (pname != 0)
> -       error ("invalid option argument %qs", pname);
> -      return defval;
> -    }
> -
> -  return atoi (p);
> -}
> -
>  #if GCC_VERSION < 3004
>
>  /* The functions floor_log2 and exact_log2 are defined as inline
> @@ -1338,7 +1311,6 @@ print_switch_values (print_switch_fn_typ
>  {
>   int pos = 0;
>   size_t j;
> -  const char **p;
>
>   /* Fill in the -frandom-seed option, if the user didn't pass it, so
>      that it can be printed below.  This helps reproducibility.  */
> @@ -1349,30 +1321,23 @@ print_switch_values (print_switch_fn_typ
>   pos = print_single_switch (print_fn, pos,
>                             SWITCH_TYPE_DESCRIPTIVE, _("options passed: "));
>
> -  for (p = &save_argv[1]; *p != NULL; p++)
> +  for (j = 1; j < save_decoded_options_count; j++)
>     {
> -      if (**p == '-')
> +      switch (save_decoded_options[j].opt_index)
>        {
> +       case OPT_o:
> +       case OPT_d:
> +       case OPT_dumpbase:
> +       case OPT_dumpdir:
> +       case OPT_auxbase:
> +       case OPT_quiet:
> +       case OPT_version:
>          /* Ignore these.  */
> -         if (strcmp (*p, "-o") == 0
> -             || strcmp (*p, "-dumpbase") == 0
> -             || strcmp (*p, "-dumpdir") == 0
> -             || strcmp (*p, "-auxbase") == 0)
> -           {
> -             if (p[1] != NULL)
> -               p++;
> -             continue;
> -           }
> -
> -         if (strcmp (*p, "-quiet") == 0
> -             || strcmp (*p, "-version") == 0)
> -           continue;
> -
> -         if ((*p)[1] == 'd')
> -           continue;
> +         continue;
>        }
>
> -      pos = print_single_switch (print_fn, pos, SWITCH_TYPE_PASSED, *p);
> +      pos = print_single_switch (print_fn, pos, SWITCH_TYPE_PASSED,
> +                                save_decoded_options[j].orig_option_with_args_text);
>     }
>
>   if (pos > 0)
> @@ -2395,14 +2360,13 @@ toplev_main (int argc, char **argv)
>  {
>   expandargv (&argc, &argv);
>
> -  save_argv = CONST_CAST2 (const char **, char **, argv);
> -
>   /* Initialization of GCC's environment, and diagnostics.  */
>   general_init (argv[0]);
>
>   /* Parse the options and do minimal processing; basically just
>      enough to default flags appropriately.  */
> -  decode_options (argc, CONST_CAST2 (const char **, char **, argv));
> +  decode_options (argc, CONST_CAST2 (const char **, char **, argv),
> +                 &save_decoded_options, &save_decoded_options_count);
>
>   init_local_tick ();
>
> Index: toplev.h
> ===================================================================
> --- toplev.h    (revision 160895)
> +++ toplev.h    (working copy)
> @@ -31,7 +31,6 @@ along with GCC; see the file COPYING3.
>    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
>
>  extern int toplev_main (int, char **);
> -extern int read_integral_parameter (const char *, const char *, const int);
>  extern void strip_off_ending (char *, int);
>  extern void _fatal_insn_not_found (const_rtx, const char *, int, const char *)
>      ATTRIBUTE_NORETURN;
> Index: testsuite/gcc.dg/opts-2.c
> ===================================================================
> --- testsuite/gcc.dg/opts-2.c   (revision 0)
> +++ testsuite/gcc.dg/opts-2.c   (revision 0)
> @@ -0,0 +1,8 @@
> +/* -O as an operand to another option should not take effect as an
> +    optimization option.  */
> +/* { dg-do compile } */
> +/* { dg-options "-I -O" } */
> +
> +#ifdef __OPTIMIZE__
> +#error "__OPTIMIZE__ defined"
> +#endif
> Index: opts.c
> ===================================================================
> --- opts.c      (revision 160895)
> +++ opts.c      (working copy)
> @@ -515,67 +515,63 @@ handle_option (int opt_index, int value,
>   return true;
>  }
>
> -/* Handle the switch beginning at ARGV for the language indicated by
> -   LANG_MASK.  Returns the number of switches consumed.  */
> -static unsigned int
> -read_cmdline_option (const char **argv, unsigned int lang_mask)
> +/* Handle the switch DECODED for the language indicated by
> +   LANG_MASK.  */
> +static void
> +read_cmdline_option (struct cl_decoded_option *decoded,
> +                    unsigned int lang_mask)
>  {
> -  struct cl_decoded_option decoded;
> -  unsigned int result;
> -  const char *opt;
>   const struct cl_option *option;
> +  const char *opt;
>
> -  opt = argv[0];
> -
> -  result = decode_cmdline_option (argv, lang_mask, &decoded);
> -  if (decoded.opt_index == cl_options_count)
> +  if (decoded->opt_index == OPT_SPECIAL_unknown)
>     {
> +      opt = decoded->arg;
> +
>       if (opt[1] == 'W' && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
>        /* We don't generate warnings for unknown -Wno-* options
>           unless we issue diagnostics.  */
> -         postpone_unknown_option_warning (argv[0]);
> +         postpone_unknown_option_warning (opt);
>       else
>        error ("unrecognized command line option %qs", opt);
> -      return result;
> +      return;
>     }
>
> -  option = &cl_options[decoded.opt_index];
> +  option = &cl_options[decoded->opt_index];
> +  opt = decoded->orig_option_with_args_text;
>
> -  if (decoded.errors & CL_ERR_DISABLED)
> +  if (decoded->errors & CL_ERR_DISABLED)
>     {
>       error ("command line option %qs"
>             " is not supported by this configuration", opt);
> -      goto done;
> +      return;
>     }
>
> -  if (decoded.errors & CL_ERR_WRONG_LANG)
> +  if (decoded->errors & CL_ERR_WRONG_LANG)
>     {
> -      complain_wrong_lang (argv[0], option, lang_mask);
> -      goto done;
> +      complain_wrong_lang (opt, option, lang_mask);
> +      return;
>     }
>
> -  if (decoded.errors & CL_ERR_MISSING_ARG)
> +  if (decoded->errors & CL_ERR_MISSING_ARG)
>     {
> -      if (!lang_hooks.missing_argument (opt, decoded.opt_index))
> +      if (!lang_hooks.missing_argument (opt, decoded->opt_index))
>        error ("missing argument to %qs", opt);
> -      goto done;
> +      return;
>     }
>
> -  if (decoded.errors & CL_ERR_UINT_ARG)
> +  if (decoded->errors & CL_ERR_UINT_ARG)
>     {
>       error ("argument to %qs should be a non-negative integer",
>             option->opt_text);
> -      goto done;
> +      return;
>     }
>
> -  gcc_assert (!decoded.errors);
> +  gcc_assert (!decoded->errors);
>
> -  if (!handle_option (decoded.opt_index, decoded.value, decoded.arg,
> +  if (!handle_option (decoded->opt_index, decoded->value, decoded->arg,
>                      lang_mask, DK_UNSPECIFIED))
>     error ("unrecognized command line option %qs", opt);
> -
> - done:
> -  return result;
>  }
>
>  /* Handle FILENAME from the command line.  */
> @@ -667,40 +663,41 @@ flag_instrument_functions_exclude_p (tre
>  }
>
>
> -/* Decode and handle the vector of command line options.  LANG_MASK
> +/* Handle the vector of command line options.  LANG_MASK
>    contains has a single bit set representing the current
>    language.  */
>  static void
> -read_cmdline_options (unsigned int argc, const char **argv, unsigned int lang_mask)
> +read_cmdline_options (struct cl_decoded_option *decoded_options,
> +                     unsigned int decoded_options_count,
> +                     unsigned int lang_mask)
>  {
> -  unsigned int n, i;
> +  unsigned int i;
>
> -  for (i = 1; i < argc; i += n)
> +  for (i = 1; i < decoded_options_count; i++)
>     {
> -      const char *opt = argv[i];
> -
> -      /* Interpret "-" or a non-switch as a file name.  */
> -      if (opt[0] != '-' || opt[1] == '\0')
> +      if (decoded_options[i].opt_index == OPT_SPECIAL_input_file)
>        {
>          if (main_input_filename == NULL)
>            {
> -             main_input_filename = opt;
> +             main_input_filename = decoded_options[i].arg;
>              main_input_baselength
>                = base_of_path (main_input_filename, &main_input_basename);
>            }
> -         add_input_filename (opt);
> -         n = 1;
> +         add_input_filename (decoded_options[i].arg);
>          continue;
>        }
>
> -      n = read_cmdline_option (argv + i, lang_mask);
> +      read_cmdline_option (decoded_options + i, lang_mask);
>     }
>  }
>
>  /* Parse command line options and set default flag values.  Do minimal
> -   options processing.  */
> +   options processing.  The decoded options are placed in *DECODED_OPTIONS
> +   and *DECODED_OPTIONS_COUNT.  */
>  void
> -decode_options (unsigned int argc, const char **argv)
> +decode_options (unsigned int argc, const char **argv,
> +               struct cl_decoded_option **decoded_options,
> +               unsigned int *decoded_options_count)
>  {
>   static bool first_time_p = true;
>   static int initial_min_crossjump_insns;
> @@ -733,40 +730,30 @@ decode_options (unsigned int argc, const
>   else
>     lang_mask = initial_lang_mask;
>
> +  decode_cmdline_options_to_array (argc, argv, lang_mask,
> +                                  decoded_options, decoded_options_count);
> +
>   /* Scan to see what optimization level has been specified.  That will
>      determine the default value of many flags.  */
> -  for (i = 1; i < argc; i++)
> +  for (i = 1; i < *decoded_options_count; i++)
>     {
> -      if (!strcmp (argv[i], "-O"))
> -       {
> -         optimize = 1;
> -         optimize_size = 0;
> -         ofast = 0;
> -       }
> -      else if (argv[i][0] == '-' && argv[i][1] == 'O')
> +      struct cl_decoded_option *opt = &(*decoded_options)[i];
> +      switch (opt->opt_index)
>        {
> -         /* Handle -Os, -O2, -O3, -O69, ...  */
> -         const char *p = &argv[i][2];
> -
> -         if ((p[0] == 's') && (p[1] == 0))
> -           {
> -             optimize_size = 1;
> -
> -             /* Optimizing for size forces optimize to be 2.  */
> -             optimize = 2;
> -             ofast = 0;
> -           }
> -         else if (strcmp (p, "fast") == 0)
> +       case OPT_O:
> +         if (*opt->arg == '\0')
>            {
> -             /* -Ofast only adds flags to -O3.  */
> +             optimize = 1;
>              optimize_size = 0;
> -             optimize = 3;
> -             ofast = 1;
> +             ofast = 0;
>            }
>          else
>            {
> -             const int optimize_val = read_integral_parameter (p, p - 2, -1);
> -             if (optimize_val != -1)
> +             const int optimize_val = integral_argument (opt->arg);
> +             if (optimize_val == -1)
> +               error ("argument to %qs should be a non-negative integer",
> +                      "-O");
> +             else
>                {
>                  optimize = optimize_val;
>                  if ((unsigned int) optimize > 255)
> @@ -775,6 +762,26 @@ decode_options (unsigned int argc, const
>                  ofast = 0;
>                }
>            }
> +         break;
> +
> +       case OPT_Os:
> +         optimize_size = 1;
> +
> +         /* Optimizing for size forces optimize to be 2.  */
> +         optimize = 2;
> +         ofast = 0;
> +         break;
> +
> +       case OPT_Ofast:
> +         /* -Ofast only adds flags to -O3.  */
> +         optimize_size = 0;
> +         optimize = 3;
> +         ofast = 1;
> +         break;
> +
> +       default:
> +         /* Ignore other options in this prescan.  */
> +         break;
>        }
>     }
>
> @@ -927,7 +934,7 @@ decode_options (unsigned int argc, const
>   OPTIMIZATION_OPTIONS (optimize, optimize_size);
>  #endif
>
> -  read_cmdline_options (argc, argv, lang_mask);
> +  read_cmdline_options (*decoded_options, *decoded_options_count, lang_mask);
>
>   if (dump_base_name && ! IS_ABSOLUTE_PATH (dump_base_name))
>     {
> @@ -2419,7 +2426,7 @@ enable_warning_as_error (const char *arg
>   new_option[0] = 'W';
>   strcpy (new_option + 1, arg);
>   option_index = find_opt (new_option, lang_mask);
> -  if (option_index == N_OPTS)
> +  if (option_index == OPT_SPECIAL_unknown)
>     {
>       error ("-Werror=%s: No option -%s", arg, new_option);
>     }
> Index: opts.h
> ===================================================================
> --- opts.h      (revision 160895)
> +++ opts.h      (working copy)
> @@ -92,7 +92,7 @@ extern const unsigned int cl_lang_count;
>
>  /* Possible ways in which a command-line option may be erroneous.
>    These do not include not being known at all; an option index of
> -   cl_options_count is used for that.  */
> +   OPT_SPECIAL_unknown is used for that.  */
>
>  #define CL_ERR_DISABLED                (1 << 0) /* Disabled in this configuration.  */
>  #define CL_ERR_MISSING_ARG     (1 << 1) /* Argument required but missing.  */
> @@ -103,12 +103,20 @@ extern const unsigned int cl_lang_count;
>
>  struct cl_decoded_option
>  {
> -  /* The index of this option, or cl_options_count if not known.  */
> +  /* The index of this option, or an OPT_SPECIAL_* value for
> +     non-options and unknown options.  */
>   size_t opt_index;
>
> -  /* The string argument, or NULL if none.  */
> +  /* The string argument, or NULL if none.  For OPT_SPECIAL_* cases,
> +     the option or non-option command-line argument.  */
>   const char *arg;
>
> +  /* The original text of option plus arguments, with separate argv
> +     elements concatenated into one string with spaces separating
> +     them.  This is for such uses as diagnostics and
> +     -frecord-gcc-switches.  */
> +  const char *orig_option_with_args_text;
> +
>   /* 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.  */
> @@ -128,11 +136,15 @@ extern unsigned num_in_fnames;
>
>  size_t find_opt (const char *input, int lang_mask);
>  extern int integral_argument (const char *arg);
> -extern unsigned int decode_cmdline_option (const char **argv,
> -                                          unsigned int lang_mask,
> -                                          struct cl_decoded_option *decoded);
> +extern 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);
>  extern void prune_options (int *argcp, char ***argvp);
> -extern void decode_options (unsigned int argc, const char **argv);
> +extern void decode_options (unsigned int argc, const char **argv,
> +                           struct cl_decoded_option **decoded_options,
> +                           unsigned int *decoded_options_count);
>  extern int option_enabled (int opt_idx);
>  extern bool get_option_state (int, struct cl_option_state *);
>  extern void set_option (int opt_index, int value, const char *arg, int);
> Index: fortran/options.c
> ===================================================================
> --- fortran/options.c   (revision 160895)
> +++ fortran/options.c   (working copy)
> @@ -541,10 +541,6 @@ gfc_handle_option (size_t scode, const c
>   int result = 1;
>   enum opt_code code = (enum opt_code) scode;
>
> -  /* Ignore file names.  */
> -  if (code == N_OPTS)
> -    return 1;
> -
>   if (gfc_cpp_handle_option (scode, arg, value) == 1)
>     return 1;
>
> Index: opth-gen.awk
> ===================================================================
> --- opth-gen.awk        (revision 160895)
> +++ opth-gen.awk        (working copy)
> @@ -358,7 +358,10 @@ for (i = 0; i < n_opts; i++) {
>        print "  " enum "," s "/* -" opts[i] " */"
>  }
>
> -print "  N_OPTS"
> +print "  N_OPTS,"
> +print "  OPT_SPECIAL_unknown,"
> +print "  OPT_SPECIAL_program_name,"
> +print "  OPT_SPECIAL_input_file"
>  print "};"
>  print ""
>  print "#endif /* OPTIONS_H */"
> Index: varasm.c
> ===================================================================
> --- varasm.c    (revision 160895)
> +++ varasm.c    (working copy)
> @@ -7199,50 +7199,11 @@ output_object_blocks (void)
>  int
>  elf_record_gcc_switches (print_switch_type type, const char * name)
>  {
> -  static char buffer[1024];
> -
> -  /* This variable is used as part of a simplistic heuristic to detect
> -     command line switches which take an argument:
> -
> -       "If a command line option does not start with a dash then
> -        it is an argument for the previous command line option."
> -
> -     This fails in the case of the command line option which is the name
> -     of the file to compile, but otherwise it is pretty reasonable.  */
> -  static bool previous_name_held_back = FALSE;
> -
>   switch (type)
>     {
>     case SWITCH_TYPE_PASSED:
> -      if (* name != '-')
> -       {
> -         if (previous_name_held_back)
> -           {
> -             unsigned int len = strlen (buffer);
> -
> -             snprintf (buffer + len, sizeof buffer - len, " %s", name);
> -             ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
> -             ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
> -             previous_name_held_back = FALSE;
> -           }
> -         else
> -           {
> -             strncpy (buffer, name, sizeof buffer);
> -             ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
> -             ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
> -           }
> -       }
> -      else
> -       {
> -         if (previous_name_held_back)
> -           {
> -             ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
> -             ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
> -           }
> -
> -         strncpy (buffer, name, sizeof buffer);
> -         previous_name_held_back = TRUE;
> -       }
> +      ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name));
> +      ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
>       break;
>
>     case SWITCH_TYPE_DESCRIPTIVE:
> @@ -7251,15 +7212,7 @@ elf_record_gcc_switches (print_switch_ty
>          /* Distinguish between invocations where name is NULL.  */
>          static bool started = false;
>
> -         if (started)
> -           {
> -             if (previous_name_held_back)
> -               {
> -                 ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
> -                 ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
> -               }
> -           }
> -         else
> +         if (!started)
>            {
>              section * sec;
>
>
> --
> Joseph S. Myers
> joseph@codesourcery.com
>
Mark Mitchell - June 20, 2010, 11:21 p.m.
Joseph S. Myers wrote:

> This patch continues work towards sharing option-processing code
> between the driver and the compilers proper by creating a function in
> opts-common.c that deals with converting the argv array into an array
> of cl_decoded_option structures.

> 2010-06-18  Joseph Myers  <joseph@codesourcery.com>
> 
> 	PR other/32998
> 	* opth-gen.awk: Generate definitions of OPT_SPECIAL_unknown,
> 	OPT_SPECIAL_program_name and OPT_SPECIAL_input_file.

...

OK.

Patch

Index: opts-common.c
===================================================================
--- opts-common.c	(revision 160895)
+++ opts-common.c	(working copy)
@@ -25,8 +25,8 @@  along with GCC; see the file COPYING3.  
 #include "options.h"
 
 /* Perform a binary search to find which option the command-line INPUT
-   matches.  Returns its index in the option array, and N_OPTS
-   (cl_options_count) on failure.
+   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
@@ -73,8 +73,8 @@  find_opt (const char *input, int lang_ma
     }
 
   /* This is the switch that is the best match but for a different
-     front end, or cl_options_count if there is no match at all.  */
-  match_wrong_lang = cl_options_count;
+     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
@@ -94,7 +94,7 @@  find_opt (const char *input, int lang_ma
 
 	  /* If we haven't remembered a prior match, remember this
 	     one.  Any prior match is necessarily better.  */
-	  if (match_wrong_lang == cl_options_count)
+	  if (match_wrong_lang == OPT_SPECIAL_unknown)
 	    match_wrong_lang = mn;
 	}
 
@@ -104,7 +104,7 @@  find_opt (const char *input, int lang_ma
     }
   while (mn != cl_options_count);
 
-  /* Return the best wrong match, or cl_options_count if none.  */
+  /* Return the best wrong match, or OPT_SPECIAL_unknown if none.  */
   return match_wrong_lang;
 }
 
@@ -129,7 +129,7 @@  integral_argument (const char *arg)
    LANG_MASK, into the structure *DECODED.  Returns the number of
    switches consumed.  */
 
-unsigned int
+static unsigned int
 decode_cmdline_option (const char **argv, unsigned int lang_mask,
 		       struct cl_decoded_option *decoded)
 {
@@ -144,7 +144,7 @@  decode_cmdline_option (const char **argv
   opt = argv[0];
 
   opt_index = find_opt (opt + 1, lang_mask | CL_COMMON | CL_TARGET);
-  if (opt_index == cl_options_count
+  if (opt_index == OPT_SPECIAL_unknown
       && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
       && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
     {
@@ -160,8 +160,11 @@  decode_cmdline_option (const char **argv
       opt_index = find_opt (opt + 1, lang_mask | CL_COMMON | CL_TARGET);
     }
 
-  if (opt_index == cl_options_count)
-    goto done;
+  if (opt_index == OPT_SPECIAL_unknown)
+    {
+      arg = argv[0];
+      goto done;
+    }
 
   option = &cl_options[opt_index];
 
@@ -169,7 +172,8 @@  decode_cmdline_option (const char **argv
      unrecognized.  */
   if (!value && (option->flags & CL_REJECT_NEGATIVE))
     {
-      opt_index = cl_options_count;
+      opt_index = OPT_SPECIAL_unknown;
+      arg = argv[0];
       goto done;
     }
 
@@ -237,9 +241,76 @@  decode_cmdline_option (const char **argv
   decoded->arg = arg;
   decoded->value = value;
   decoded->errors = errors;
+  switch (result)
+    {
+    case 1:
+      decoded->orig_option_with_args_text = argv[0];
+      break;
+    case 2:
+      decoded->orig_option_with_args_text = concat (argv[0], " ",
+						    argv[1], NULL);
+      break;
+    default:
+      gcc_unreachable ();
+    }
   return result;
 }
 
+/* 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
+   applicable for decoding.  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;
+
+  opt_array = XNEWVEC (struct cl_decoded_option, argc);
+
+  opt_array[0].opt_index = OPT_SPECIAL_program_name;
+  opt_array[0].arg = argv[0];
+  opt_array[0].orig_option_with_args_text = argv[0];
+  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')
+	{
+	  opt_array[num_decoded_options].opt_index = OPT_SPECIAL_input_file;
+	  opt_array[num_decoded_options].arg = opt;
+	  opt_array[num_decoded_options].orig_option_with_args_text = opt;
+	  opt_array[num_decoded_options].value = 1;
+	  opt_array[num_decoded_options].errors = 0;
+	  num_decoded_options++;
+	  n = 1;
+	  continue;
+	}
+
+      n = decode_cmdline_option (argv + i, lang_mask,
+				 &opt_array[num_decoded_options]);
+      num_decoded_options++;
+    }
+
+  opt_array = XRESIZEVEC (struct cl_decoded_option, opt_array,
+			  num_decoded_options);
+  *decoded_options = opt_array;
+  *decoded_options_count = num_decoded_options;
+}
+
 /* Return true if NEXT_OPT_IDX cancels OPT_IDX.  Return false if the
    next one is the same as ORIG_NEXT_OPT_IDX.  */
 
@@ -281,7 +352,7 @@  prune_options (int *argcp, char ***argvp
       const char *opt = (*argvp) [i];
 
       opt_index = find_opt (opt + 1, -1);
-      if (opt_index == cl_options_count
+      if (opt_index == OPT_SPECIAL_unknown
 	  && (opt[1] == 'W' || opt[1] == 'f' || opt[1] == 'm')
 	  && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
 	{
@@ -300,7 +371,7 @@  prune_options (int *argcp, char ***argvp
 	  free (dup);
 	}
 
-      if (opt_index == cl_options_count)
+      if (opt_index == OPT_SPECIAL_unknown)
 	{
 cont:
 	  options [i] = 0;
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 160895)
+++ c-family/c-common.c	(working copy)
@@ -7624,6 +7624,8 @@  parse_optimize_options (tree args, bool 
   unsigned i;
   int saved_flag_strict_aliasing;
   const char **opt_argv;
+  struct cl_decoded_option *decoded_options;
+  unsigned int decoded_options_count;
   tree ap;
 
   /* Build up argv vector.  Just in case the string is stored away, use garbage
@@ -7716,7 +7718,8 @@  parse_optimize_options (tree args, bool 
   saved_flag_strict_aliasing = flag_strict_aliasing;
 
   /* Now parse the options.  */
-  decode_options (opt_argc, opt_argv);
+  decode_options (opt_argc, opt_argv, &decoded_options,
+		  &decoded_options_count);
 
   targetm.override_options_after_change();
 
Index: toplev.c
===================================================================
--- toplev.c	(revision 160895)
+++ toplev.c	(working copy)
@@ -126,8 +126,9 @@  static bool no_backend;
 /* Length of line when printing switch values.  */
 #define MAX_LINE 75
 
-/* Copy of argument vector to toplev_main.  */
-static const char **save_argv;
+/* Decoded options, and number of such options.  */
+static struct cl_decoded_option *save_decoded_options;
+static unsigned int save_decoded_options_count;
 
 /* Name of top-level original source file (what was input to cpp).
    This comes from the #-command at the beginning of the actual input.
@@ -488,34 +489,6 @@  set_random_seed (const char *val)
   return old;
 }
 
-/* Decode the string P as an integral parameter.
-   If the string is indeed an integer return its numeric value else
-   issue an Invalid Option error for the option PNAME and return DEFVAL.
-   If PNAME is zero just return DEFVAL, do not call error.  */
-
-int
-read_integral_parameter (const char *p, const char *pname, const int  defval)
-{
-  const char *endp = p;
-
-  while (*endp)
-    {
-      if (ISDIGIT (*endp))
-	endp++;
-      else
-	break;
-    }
-
-  if (*endp != 0)
-    {
-      if (pname != 0)
-	error ("invalid option argument %qs", pname);
-      return defval;
-    }
-
-  return atoi (p);
-}
-
 #if GCC_VERSION < 3004
 
 /* The functions floor_log2 and exact_log2 are defined as inline
@@ -1338,7 +1311,6 @@  print_switch_values (print_switch_fn_typ
 {
   int pos = 0;
   size_t j;
-  const char **p;
 
   /* Fill in the -frandom-seed option, if the user didn't pass it, so
      that it can be printed below.  This helps reproducibility.  */
@@ -1349,30 +1321,23 @@  print_switch_values (print_switch_fn_typ
   pos = print_single_switch (print_fn, pos,
 			     SWITCH_TYPE_DESCRIPTIVE, _("options passed: "));
 
-  for (p = &save_argv[1]; *p != NULL; p++)
+  for (j = 1; j < save_decoded_options_count; j++)
     {
-      if (**p == '-')
+      switch (save_decoded_options[j].opt_index)
 	{
+	case OPT_o:
+	case OPT_d:
+	case OPT_dumpbase:
+	case OPT_dumpdir:
+	case OPT_auxbase:
+	case OPT_quiet:
+	case OPT_version:
 	  /* Ignore these.  */
-	  if (strcmp (*p, "-o") == 0
-	      || strcmp (*p, "-dumpbase") == 0
-	      || strcmp (*p, "-dumpdir") == 0
-	      || strcmp (*p, "-auxbase") == 0)
-	    {
-	      if (p[1] != NULL)
-		p++;
-	      continue;
-	    }
-
-	  if (strcmp (*p, "-quiet") == 0
-	      || strcmp (*p, "-version") == 0)
-	    continue;
-
-	  if ((*p)[1] == 'd')
-	    continue;
+	  continue;
 	}
 
-      pos = print_single_switch (print_fn, pos, SWITCH_TYPE_PASSED, *p);
+      pos = print_single_switch (print_fn, pos, SWITCH_TYPE_PASSED,
+				 save_decoded_options[j].orig_option_with_args_text);
     }
 
   if (pos > 0)
@@ -2395,14 +2360,13 @@  toplev_main (int argc, char **argv)
 {
   expandargv (&argc, &argv);
 
-  save_argv = CONST_CAST2 (const char **, char **, argv);
-
   /* Initialization of GCC's environment, and diagnostics.  */
   general_init (argv[0]);
 
   /* Parse the options and do minimal processing; basically just
      enough to default flags appropriately.  */
-  decode_options (argc, CONST_CAST2 (const char **, char **, argv));
+  decode_options (argc, CONST_CAST2 (const char **, char **, argv),
+		  &save_decoded_options, &save_decoded_options_count);
 
   init_local_tick ();
 
Index: toplev.h
===================================================================
--- toplev.h	(revision 160895)
+++ toplev.h	(working copy)
@@ -31,7 +31,6 @@  along with GCC; see the file COPYING3.  
    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
 
 extern int toplev_main (int, char **);
-extern int read_integral_parameter (const char *, const char *, const int);
 extern void strip_off_ending (char *, int);
 extern void _fatal_insn_not_found (const_rtx, const char *, int, const char *)
      ATTRIBUTE_NORETURN;
Index: testsuite/gcc.dg/opts-2.c
===================================================================
--- testsuite/gcc.dg/opts-2.c	(revision 0)
+++ testsuite/gcc.dg/opts-2.c	(revision 0)
@@ -0,0 +1,8 @@ 
+/* -O as an operand to another option should not take effect as an
+    optimization option.  */
+/* { dg-do compile } */
+/* { dg-options "-I -O" } */
+
+#ifdef __OPTIMIZE__
+#error "__OPTIMIZE__ defined"
+#endif
Index: opts.c
===================================================================
--- opts.c	(revision 160895)
+++ opts.c	(working copy)
@@ -515,67 +515,63 @@  handle_option (int opt_index, int value,
   return true;
 }
 
-/* Handle the switch beginning at ARGV for the language indicated by
-   LANG_MASK.  Returns the number of switches consumed.  */
-static unsigned int
-read_cmdline_option (const char **argv, unsigned int lang_mask)
+/* Handle the switch DECODED for the language indicated by
+   LANG_MASK.  */
+static void
+read_cmdline_option (struct cl_decoded_option *decoded,
+		     unsigned int lang_mask)
 {
-  struct cl_decoded_option decoded;
-  unsigned int result;
-  const char *opt;
   const struct cl_option *option;
+  const char *opt;
 
-  opt = argv[0];
-
-  result = decode_cmdline_option (argv, lang_mask, &decoded);
-  if (decoded.opt_index == cl_options_count)
+  if (decoded->opt_index == OPT_SPECIAL_unknown)
     {
+      opt = decoded->arg;
+
       if (opt[1] == 'W' && opt[2] == 'n' && opt[3] == 'o' && opt[4] == '-')
 	/* We don't generate warnings for unknown -Wno-* options
 	   unless we issue diagnostics.  */
-	  postpone_unknown_option_warning (argv[0]);
+	  postpone_unknown_option_warning (opt);
       else
 	error ("unrecognized command line option %qs", opt);
-      return result;
+      return;
     }
 
-  option = &cl_options[decoded.opt_index];
+  option = &cl_options[decoded->opt_index];
+  opt = decoded->orig_option_with_args_text;
 
-  if (decoded.errors & CL_ERR_DISABLED)
+  if (decoded->errors & CL_ERR_DISABLED)
     {
       error ("command line option %qs"
 	     " is not supported by this configuration", opt);
-      goto done;
+      return;
     }
 
-  if (decoded.errors & CL_ERR_WRONG_LANG)
+  if (decoded->errors & CL_ERR_WRONG_LANG)
     {
-      complain_wrong_lang (argv[0], option, lang_mask);
-      goto done;
+      complain_wrong_lang (opt, option, lang_mask);
+      return;
     }
 
-  if (decoded.errors & CL_ERR_MISSING_ARG)
+  if (decoded->errors & CL_ERR_MISSING_ARG)
     {
-      if (!lang_hooks.missing_argument (opt, decoded.opt_index))
+      if (!lang_hooks.missing_argument (opt, decoded->opt_index))
 	error ("missing argument to %qs", opt);
-      goto done;
+      return;
     }
 
-  if (decoded.errors & CL_ERR_UINT_ARG)
+  if (decoded->errors & CL_ERR_UINT_ARG)
     {
       error ("argument to %qs should be a non-negative integer",
 	     option->opt_text);
-      goto done;
+      return;
     }
 
-  gcc_assert (!decoded.errors);
+  gcc_assert (!decoded->errors);
 
-  if (!handle_option (decoded.opt_index, decoded.value, decoded.arg,
+  if (!handle_option (decoded->opt_index, decoded->value, decoded->arg,
 		      lang_mask, DK_UNSPECIFIED))
     error ("unrecognized command line option %qs", opt);
-
- done:
-  return result;
 }
 
 /* Handle FILENAME from the command line.  */
@@ -667,40 +663,41 @@  flag_instrument_functions_exclude_p (tre
 }
 
 
-/* Decode and handle the vector of command line options.  LANG_MASK
+/* Handle the vector of command line options.  LANG_MASK
    contains has a single bit set representing the current
    language.  */
 static void
-read_cmdline_options (unsigned int argc, const char **argv, unsigned int lang_mask)
+read_cmdline_options (struct cl_decoded_option *decoded_options,
+		      unsigned int decoded_options_count,
+		      unsigned int lang_mask)
 {
-  unsigned int n, i;
+  unsigned int i;
 
-  for (i = 1; i < argc; i += n)
+  for (i = 1; i < decoded_options_count; i++)
     {
-      const char *opt = argv[i];
-
-      /* Interpret "-" or a non-switch as a file name.  */
-      if (opt[0] != '-' || opt[1] == '\0')
+      if (decoded_options[i].opt_index == OPT_SPECIAL_input_file)
 	{
 	  if (main_input_filename == NULL)
 	    {
-	      main_input_filename = opt;
+	      main_input_filename = decoded_options[i].arg;
 	      main_input_baselength
 		= base_of_path (main_input_filename, &main_input_basename);
 	    }
-	  add_input_filename (opt);
-	  n = 1;
+	  add_input_filename (decoded_options[i].arg);
 	  continue;
 	}
 
-      n = read_cmdline_option (argv + i, lang_mask);
+      read_cmdline_option (decoded_options + i, lang_mask);
     }
 }
 
 /* Parse command line options and set default flag values.  Do minimal
-   options processing.  */
+   options processing.  The decoded options are placed in *DECODED_OPTIONS
+   and *DECODED_OPTIONS_COUNT.  */
 void
-decode_options (unsigned int argc, const char **argv)
+decode_options (unsigned int argc, const char **argv,
+		struct cl_decoded_option **decoded_options,
+		unsigned int *decoded_options_count)
 {
   static bool first_time_p = true;
   static int initial_min_crossjump_insns;
@@ -733,40 +730,30 @@  decode_options (unsigned int argc, const
   else
     lang_mask = initial_lang_mask;
 
+  decode_cmdline_options_to_array (argc, argv, lang_mask,
+				   decoded_options, decoded_options_count);
+
   /* Scan to see what optimization level has been specified.  That will
      determine the default value of many flags.  */
-  for (i = 1; i < argc; i++)
+  for (i = 1; i < *decoded_options_count; i++)
     {
-      if (!strcmp (argv[i], "-O"))
-	{
-	  optimize = 1;
-	  optimize_size = 0;
-	  ofast = 0;
-	}
-      else if (argv[i][0] == '-' && argv[i][1] == 'O')
+      struct cl_decoded_option *opt = &(*decoded_options)[i];
+      switch (opt->opt_index)
 	{
-	  /* Handle -Os, -O2, -O3, -O69, ...  */
-	  const char *p = &argv[i][2];
-
-	  if ((p[0] == 's') && (p[1] == 0))
-	    {
-	      optimize_size = 1;
-
-	      /* Optimizing for size forces optimize to be 2.  */
-	      optimize = 2;
-	      ofast = 0;
-	    }
-	  else if (strcmp (p, "fast") == 0)
+	case OPT_O:
+	  if (*opt->arg == '\0')
 	    {
-	      /* -Ofast only adds flags to -O3.  */
+	      optimize = 1;
 	      optimize_size = 0;
-	      optimize = 3;
-	      ofast = 1;
+	      ofast = 0;
 	    }
 	  else
 	    {
-	      const int optimize_val = read_integral_parameter (p, p - 2, -1);
-	      if (optimize_val != -1)
+	      const int optimize_val = integral_argument (opt->arg);
+	      if (optimize_val == -1)
+		error ("argument to %qs should be a non-negative integer",
+		       "-O");
+	      else
 		{
 		  optimize = optimize_val;
 		  if ((unsigned int) optimize > 255)
@@ -775,6 +762,26 @@  decode_options (unsigned int argc, const
 		  ofast = 0;
 		}
 	    }
+	  break;
+
+	case OPT_Os:
+	  optimize_size = 1;
+
+	  /* Optimizing for size forces optimize to be 2.  */
+	  optimize = 2;
+	  ofast = 0;
+	  break;
+
+	case OPT_Ofast:
+	  /* -Ofast only adds flags to -O3.  */
+	  optimize_size = 0;
+	  optimize = 3;
+	  ofast = 1;
+	  break;
+
+	default:
+	  /* Ignore other options in this prescan.  */
+	  break;
 	}
     }
 
@@ -927,7 +934,7 @@  decode_options (unsigned int argc, const
   OPTIMIZATION_OPTIONS (optimize, optimize_size);
 #endif
 
-  read_cmdline_options (argc, argv, lang_mask);
+  read_cmdline_options (*decoded_options, *decoded_options_count, lang_mask);
 
   if (dump_base_name && ! IS_ABSOLUTE_PATH (dump_base_name))
     {
@@ -2419,7 +2426,7 @@  enable_warning_as_error (const char *arg
   new_option[0] = 'W';
   strcpy (new_option + 1, arg);
   option_index = find_opt (new_option, lang_mask);
-  if (option_index == N_OPTS)
+  if (option_index == OPT_SPECIAL_unknown)
     {
       error ("-Werror=%s: No option -%s", arg, new_option);
     }
Index: opts.h
===================================================================
--- opts.h	(revision 160895)
+++ opts.h	(working copy)
@@ -92,7 +92,7 @@  extern const unsigned int cl_lang_count;
 
 /* Possible ways in which a command-line option may be erroneous.
    These do not include not being known at all; an option index of
-   cl_options_count is used for that.  */
+   OPT_SPECIAL_unknown is used for that.  */
 
 #define CL_ERR_DISABLED		(1 << 0) /* Disabled in this configuration.  */
 #define CL_ERR_MISSING_ARG	(1 << 1) /* Argument required but missing.  */
@@ -103,12 +103,20 @@  extern const unsigned int cl_lang_count;
 
 struct cl_decoded_option
 {
-  /* The index of this option, or cl_options_count if not known.  */
+  /* The index of this option, or an OPT_SPECIAL_* value for
+     non-options and unknown options.  */
   size_t opt_index;
 
-  /* The string argument, or NULL if none.  */
+  /* The string argument, or NULL if none.  For OPT_SPECIAL_* cases,
+     the option or non-option command-line argument.  */
   const char *arg;
 
+  /* The original text of option plus arguments, with separate argv
+     elements concatenated into one string with spaces separating
+     them.  This is for such uses as diagnostics and
+     -frecord-gcc-switches.  */
+  const char *orig_option_with_args_text;
+
   /* 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.  */
@@ -128,11 +136,15 @@  extern unsigned num_in_fnames;
 
 size_t find_opt (const char *input, int lang_mask);
 extern int integral_argument (const char *arg);
-extern unsigned int decode_cmdline_option (const char **argv,
-					   unsigned int lang_mask,
-					   struct cl_decoded_option *decoded);
+extern 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);
 extern void prune_options (int *argcp, char ***argvp);
-extern void decode_options (unsigned int argc, const char **argv);
+extern void decode_options (unsigned int argc, const char **argv,
+			    struct cl_decoded_option **decoded_options,
+			    unsigned int *decoded_options_count);
 extern int option_enabled (int opt_idx);
 extern bool get_option_state (int, struct cl_option_state *);
 extern void set_option (int opt_index, int value, const char *arg, int);
Index: fortran/options.c
===================================================================
--- fortran/options.c	(revision 160895)
+++ fortran/options.c	(working copy)
@@ -541,10 +541,6 @@  gfc_handle_option (size_t scode, const c
   int result = 1;
   enum opt_code code = (enum opt_code) scode;
 
-  /* Ignore file names.  */
-  if (code == N_OPTS)
-    return 1;
-
   if (gfc_cpp_handle_option (scode, arg, value) == 1)
     return 1;
 
Index: opth-gen.awk
===================================================================
--- opth-gen.awk	(revision 160895)
+++ opth-gen.awk	(working copy)
@@ -358,7 +358,10 @@  for (i = 0; i < n_opts; i++) {
 	print "  " enum "," s "/* -" opts[i] " */"
 }
 
-print "  N_OPTS"
+print "  N_OPTS,"
+print "  OPT_SPECIAL_unknown,"
+print "  OPT_SPECIAL_program_name,"
+print "  OPT_SPECIAL_input_file"
 print "};"
 print ""
 print "#endif /* OPTIONS_H */"
Index: varasm.c
===================================================================
--- varasm.c	(revision 160895)
+++ varasm.c	(working copy)
@@ -7199,50 +7199,11 @@  output_object_blocks (void)
 int
 elf_record_gcc_switches (print_switch_type type, const char * name)
 {
-  static char buffer[1024];
-
-  /* This variable is used as part of a simplistic heuristic to detect
-     command line switches which take an argument:
-
-       "If a command line option does not start with a dash then
-        it is an argument for the previous command line option."
-
-     This fails in the case of the command line option which is the name
-     of the file to compile, but otherwise it is pretty reasonable.  */
-  static bool previous_name_held_back = FALSE;
-
   switch (type)
     {
     case SWITCH_TYPE_PASSED:
-      if (* name != '-')
-	{
-	  if (previous_name_held_back)
-	    {
-	      unsigned int len = strlen (buffer);
-
-	      snprintf (buffer + len, sizeof buffer - len, " %s", name);
-	      ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
-	      ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
-	      previous_name_held_back = FALSE;
-	    }
-	  else
-	    {
-	      strncpy (buffer, name, sizeof buffer);
-	      ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
-	      ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
-	    }
-	}
-      else
-	{
-	  if (previous_name_held_back)
-	    {
-	      ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
-	      ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
-	    }
-
-	  strncpy (buffer, name, sizeof buffer);
-	  previous_name_held_back = TRUE;
-	}
+      ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name));
+      ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
       break;
 
     case SWITCH_TYPE_DESCRIPTIVE:
@@ -7251,15 +7212,7 @@  elf_record_gcc_switches (print_switch_ty
 	  /* Distinguish between invocations where name is NULL.  */
 	  static bool started = false;
 
-	  if (started)
-	    {
-	      if (previous_name_held_back)
-		{
-		  ASM_OUTPUT_ASCII (asm_out_file, buffer, strlen (buffer));
-		  ASM_OUTPUT_SKIP (asm_out_file, (unsigned HOST_WIDE_INT) 1);
-		}
-	    }
-	  else
+	  if (!started)
 	    {
 	      section * sec;