Compiler Switch Changes in Visual C++ 2005
One noticeable change in the coming release of the Visual C++ compiler is the
changes to compiler switches. If you use the project system, when the IDE
upgrades your project files, many of these changes will be taken care of
automatically. However, if you use another build system like nmake, you might
have to fix some build issues.
Why did we remove and deprecate some compiler switches?
The Visual C++ compiler has been around a long time. Long enough that many
features that were once very useful are no longer needed (optimizations for the
486 probably don't get used that much these days). In some cases, new switches
have been added that mean exactly the same thing as an existing switch.
Furthermore, some switches existed that are essentially evil and easily led to
the program errors.
Much of the investigation here began when we started thinking harder about
security issues during the development of Visual C++ 2003. In particular, as
soon as potential security issues became a higher priority than backwards
compatibility, we had to justify more of the feature set Visual C++ exposed and
of course we had to question how much testing we actually did on all the
configurations the compiler could exercise. The first change was modest. Visual
C++ 2003 deprecated the
Gf switch. In that compiler, you would have seen the
c1xx : warning C4349: /Gf is deprecated and will not be supported in future
versions of Visual C++; remove /Gf or use /GF instead
For everyone who does not immediately recall what the
switch does, it pooled all the string literals into a writable page of memory.
Just the description screams of security issues if not simple reliability
issues. The alternative
GF switch pools string literals into a
read-only page of memory, which will break C programs that mutate string
literals. Of course, pooling is a great way to reduce the size of a binary.
Goals behind the switch reduction
The changes to the switch model for Visual C++ 2005 came to be known as
switch reduction. It was designed along with another improvement, warning
families,that did not make it into this release of Visual C++.
Hopefully, warning families will make its way into the next release. But I'll
delay discussion of that since we're considering switches right now.
When I started to write the specification for the changes in this area, I
listed the following four requirements:
- Removing the switch would benefit testing, by reducing the test matrix for the
- Removing the switch would lead to a better user experience.
- Changing switches would make multiple versions and platforms have
- After considering the above, removing or changing the switch does not place
undue burden on users.
I also noted in the specification that changes to switches needed to
happen swiftly to avoid prolonged impact on users. Unfortunately, making changes
to compiler switches turned out to be harder than I ever imagined. Fixing all of
the files in the test harness took the majority of our effort. As a result, all
of the work called for in the switch reduction specification wasn't completed.
When it came to improved user experience, the focus was on making it easy for
programmers to select the correct defaults for their program or at least to easily
understand the interactions switches have with each other. A good example of
this are the
Ot switches, which mean "favor code
space" and "favor code speed" respectively. Neither of these switches do
anything unless the
Og switch is also given to the compiler. This
issue frequently comes up. We can either fix it by warning the user that he
probably did something wrong, or we can make the switch model intuitive. Given
the overall complexity of just getting a standard C++ program to run correctly,
I favor making the compiler more intuitive and making it work by default.
The third requirement addresses the problem with the
G7 compiler switches. In a good
attempt to allow application writers to make use of the latest and greatest
hardware, nearly every compiler introduced a new better switch to make your
code even faster. Unfortunately, compiler switches end up in make files that rarely
get revised. It wasn't uncommon to see a make file specify a
G5 switch even though 80486 and Pentium have long been out of mainstream
production. The G-series of switches do not prevent programs from running on older
hardware, which was a common misconception. Eventually, Visual C++ just ignored the
G5 switches and the program
compiled as if
G6 had been given to the compiler. I'll say more on what
changed with these switches below.
Choosing what to do and getting it done
Throughout this process, I acquired a reputation for not liking switches. A
running joke in the Visual C++ product team was to suggest adding a switch to
me. To be honest, the reputation wasn't quite deserved. I just challenged the
notion of adding a switch with its necessity. To many times, switches were added
to the compiler to cover over other problems. In fact, that's par for the
industry. Most compilers offer dozens of switches to disable particular
optimizations because they break code. That's lazy – the
programmer using the compiler has to understand the impact of an optimization
and even if he does, he's likely to overlook something. The Visual C++
team has a much higher aspiration to make the compiler useful to every
application and systems programmer on Windows, not just geniuses with intimate
knowledge of every version of the compiler.
All that said, I started evaluating changes by doing none else than reading
the source code for the compiler driver. Through that I came across obsolete,
outdated, bizarre, undocumented, and useless switches. I looked at each one
asking whether it was necessary for the compiler in the long term and evaluating
each switch against the requirements listed above. I actually spent most of my
time trying to figure out what each switch did. Even asking developers who work
on the compiler, I'd sometimes get several different answers. In a few cases, no
one knew what the switch did. If our own team couldn't recall a switch's purpose,
it's not hard to believe nearly every programmer using Visual C++ will have the
Out of this process came the first list of changes to the switch model. Team
members were given opportunity to comment along with the Visual C++ MVPs.
Through much discussion, the list was narrowed to what we originally
implemented. Early usage of the compiler in alphas and the first beta yielded feedback
that made us reconsider some of the changes. In several cases we reverted
changes due to direct feedback.
Before I go into more detail about some of the specific changes, a primary
concern was ease of migration. The most noticeable impact of a change like this
is breaking a build. Fortunately, additional warnings about compiler switches
usually come from the compiler driver cl.exe instead of the compiler itself (one
of c1.dll, c1xx.dll, or c2.dll). This is usually noticeable
by the message
Dxxxx instead of
Cxxxx. As a result, the
WX switch does not cause a build failure when the driver warns about an
unrecognized or deprecated switch.
Now let's look at some of the specific changes...
As I mentioned earlier, the G-series of switches offered two problem points.
First, many customers confused the meaning of these switches, thinking that it
was necessary to throw
G4 to make the compiler produce code that
could still run on a 486 computer. Second, the switches tended to stay in make
files well past the intended period of time. In recent versions of Visual C++,
G5 switches were just
G7 were the only two that made a
difference, the natural question was what the difference between these two were.
It turns out there was very little difference, and there were ways to generate
code that resulted in great performance on both Pentium III and and all the new
processors coming from both Intel and AMD. The switches weren't really necessary.
So, we removed them and reduced the testing matrix at the same time.
So far I've taken an x86 focused view of the world. On IA64, there are
significant differences between first generation processors and the following
generations. IA64 compilers continue to have the
switch because the usability problems haven't shown up on IA64, partly because
the market of developers is relatively tiny compared to x86.
On x64 platforms though, we tried at first to avoid generating different code
between compilers. In the end, we were compelled to support specific processors.
To avoid any confusion, rather than continue with the G-series of switches, the
x64 compiler has the
switches. It should now be clear that throwing one of these switches still
allows the program to run on other processors, but likely with weaker
Certainly, I would have preferred to see the same switch set on all
compilers. That hasn't happened yet, and it hasn't been necessary to drive that
idea to the top of my priorities.
Regarding other optimization switches, I did a cognitive walkthrough of the
optimization settings shown in the
cl /? results. These were some of my
O2are almost identical. They differ only in the fact that
Gy. There is almost no reason to avoid throwing these two
- It is difficult to know that
Oiare not thrown, which is usually only
useful for debugging purposes – it is not the starting point for a release
build, and it's better to use pragmas for this level of control rather than
changing the build system.
At the time, I wanted to converge the optimization settings so that multiple
switches resulted in the same behavior. This would allow build scripts to
continue working without changes, but would both reduce the test matrix and
improve default compiler switch selection. This is what I had proposed
Os: now turns on optimization, favoring size (also throws
Ot: now turns on optimization, favoring speed (also throws
O1: has exactly the same meaning as
O2: has exactly the same meaning as
Od: disables optimization (also throws
Ox: has exactly the same meaning as
- Remove the
If you're using Beta 2 of Visual C++ 2005, you'll see that this convergence
of optimization switches did not happen. That's mostly because we didn't have
enough time in the schedule to do so. Despite known usability issues with
optimization switches, we don't currently have plans to complete this part of
the switch reduction specification in the next release.
It may come as a shock, but some of the features we've released in Visual C++
haven't had the level of quality that we wish it would have. The most notable of
all is automatic precompiled header (PCH) files. The
YX switch was
used to tell the compiler to automatically select and create a PCH file.
Developers would use this to speed up a build, but in practice it slowed the
build down. Only in a few test cases did it benefit the build time. Given that
information, we decided to remove the switch. If you do want to use a PCH, the
Yu switch still exist and, when used correctly, they will dramatically
improve build times.
There were a few other undocumented switches left over from experiments that
never showed results. We took the opportunity to at least deprecate these
I also listed the
Wp64 switch to be deprecated and turned on by
default, which doesn't appear to have happened. Overall, the
switch is no longer necessary. Visual Studio 2005 now includes 64-bit compilers.
Compiling code with a 64-bit compiler yields accurate warnings and errors,
Wp64 option only yielded approximations and in many
cases had false positives and false negatives.
Visual C++ 2005 introduces at least three new switches for CLR modes. All told,
we have the following CLR modes:
clr-: This corresponds to not using CLR functionality at all. It
is the default.
clr: This tells the compiler to enable CLR
funcationality, using the new syntax, and to produce a mixed executable image
(one that can contain both machine code and MSIL). Object files generated from
this mode can be linked with object files compiled with the
clr:oldSyntax: This tells the compiler to enable CLR functionality,
using the old managed syntax, and to produce a mixed executable image.
clr:pure: This tells the compiler to enable CLR functionality and to
produce a pure executable image (one that contains only MSIL).
clr:safe: This tells the compiler to enable CLR
functionality, to produce a pure executable image, and to only allow
verifiable source code through the compiler.
I'd say selecting the names for these switches was the hardest part of the
process. I heard concerns that "old syntax" could be insulting to
geriatric constituents. That was perhaps the highlight of the conversation.
The "safe" switch had equally heated debate as concerns would be raised
that Standard C++ isn't safe. The choice of "clr:safe" was for
consistency with other .NET languages, which typically have an "unsafe" switch.
"pure" has similar subjective concerns. Of course, it's too late to change the
names now... we'll all have to live with the ones listed above.
Each of these switches override each other, so it's not possible to mix these
modes (for example, old syntax and verifiable code cannot be mixed). In previous
releases, there was also the
clr:noAssembly switch. In reality,
that should have been a linker switch since the compiler generates object files,
not assemblies. Thus, in Visual C++ 2005, the
LN switch replaces
The other CLR switch,
clr:initialAppDomain, was originally
scheduled to be removed from Visual C++ 2005 since it was there for
compatibility with CLR 1.0. It turns out that there are actually useful things
one can do with this switch with CLR 2.0. We discovered that late in the feedback cycle, so
the switch was left alone. Ideally, it would have been replaced with a switch
G since it affects code generation.
The introduction of three new modes does add testing burden as the
matrix of test combinations now quadruples. In reality, we needed to focus
testing on all the CLR modes to bring it up to the same level of quality as
unmanaged code. One of the first things we noticed was that C code compiled with
any CLR option made very little sense. C doesn't have namespaces which makes it
obviously unable to access any of the .NET Frameworks. Compiling C code with the
clr switch was nothing more than an interesting experiment... an
experiment that unnecessarily expanded the test matrix. Thus, we made
TC conflict with all of the CLR modes.
Conformance switches and default behavior
After the work in Visual C++ 2003 to support more Standard C++ features, a common
complaint was that standard conforming code that should compile and run did not. It almost always
came down to a particular switch was not thrown. To that effect, there were
semantic differences between strict compliance mode
Za and the
Ze. I'm a strong believer that different modes in a
compiler should at most add and remove features from the language
– they should never change the semantics of the language.
Here are examples of where that principle is violated in Visual C++ 2003:
- Compiling with and without
Zc:wchar_tchanges the meaning
wchar_ttype, which impacts overload resolution, name
decoration, and binary compatibility.
- Compiling with and without
Zc:forScopechanges the meaning of
the iterator variable in a for loop.
- Compiling with
whether stack allocated C++ objects will have their destructors called in
the event of an exception.
- Compiling with and without
Jchanges the meaning of
- Compiling with
binary compatibility and changes the capabilities of virtual inheritance.
Even when we were almost doing the right thing, as in the case of
(enable dynamic type info), Visual C++ had the wrong default. In the past, the
compiler defaulted to no dynamic type info. The result of all of this is that
Visual C++ wasn't standards conformant out of the box. You had to set a handful
of compiler switches to get top notch conformance with the C++ standard.
Unfortunately, whenever you set all these switches, many libraries no longer
So, while Visual C++ 2005 hasn't been on the bleeding edge of adding dozens
of new standard features, we have been getting more and more libraries to
compile cleanly with a mostly conformant mode. Visual C++ 2005 now makes the
following switches on by default:
GS. The one
switch I wish we had made a defult but we didn't get to was
(enable conformant exception handling). Because some new defaults certainly
break code, new switches were added to override them, including
Zc:forScope-. Some of these new
switches are deprecated off the bat since you are better off fixing code than
just ignoring problems.
At the same time, I have problems with the
Za switch. At first,
it makes some sense, but very few people actually write the entire program in
the restrictive subset that is Standard C++. More often, they want parts of the
program like the core engine to be standard compliant. Just as using
was a bad solution to diagnose portability of code between 32-bit and 64-bit,
Za to diagnose portability between Visual C++ and other
compilers is a truly bad model. If you care about portability, you'll compile
your code with multiple compilers on a regular basis. The
turns out to be completely unnecessary because it is the default mode of the
compiler and it is not possible to override the
Za switch. Thus,
Ze switch is deprecated in Visual C++ 2005. In the long term, I
hope that the default mode will be able to compile any code that
can with exactly the same behavior, thus making
Za unnecessary. For
ease of diagnosing non-standard extensions, the warning families feature I spoke
about earlier is a far better solution than the restrictive
Truly evil switches
As I mentioned in the introduction, the
Gf switch is particularly
bad since it pools string literals into a writable section of memory. C
programs are allowed to mutate string literals, so a program that pools two
unrelated string literals can have unexpected results when a string is mutated.
Visual C++ 2003 deprecated this option, and it is now removed from Visual C++
2005. C++ isn't affected by this change to the same degree that C is because C++
specifies that string literals shall not be mutated, and thus the compiler
always allocates them in read-only memory.
Another switch that just should never be used is
H. This switch
placed a maximum length on external names, which was achieved by just truncating
the names. The result is that multiple entities could have the same name. Even
more, undecorating the names in the debugger was impossible. Really, nothing
good could come out of using the
H compiler switch, so it was
Yet another example are the
These switches gave the compiler the freedom to make assumptions about memory
aliasing that were often untrue. As a result, the optimizer made an optimization
that broke programs in subtle ways. Very few programs could actually work with
these options, so they were both removed from Visual C++ 2005.
Switches that had customer feedback
Originally, we deprecated the
J switch. The switch changes the meaning of
char to mean
unsigned char instead of
signed char. A program that always specifies
unsigned in front of
char would never be affected by this compiler switch; however,
nearly no program does. System headers should be vigilant and always specify
char is signed or unsigned in source code. That again is
where warning families can provide a solution. Anyways, we deprecated the switch
understanding that is probably could never be removed (it can be useful in
migrating source code from other operating systems). After a while, the feedback
on this one compiler switch became so overwhelming that we're leaving
Another set of switches that had similar, if not thundering feedback, are the
vd switches. These control the binary layout for objects to enable
virtual inheritance. I personally find these switches quite distasteful because
it prevents code from interoperating, but we had feedback that a particular
virtual inheritance scenario didn't work. The only solution given the binary
format we had was to introduce the
vd2 compiler switch. So, in my
opinion, the situation got even worse, but I don't see a better solution. C'est
Switches that had replacements
In several cases, switches have been replaced by a group of switches. A good
example is the
GX switch which ended up being replaced by
EHsc when all the
EH switches were added.
stuck around for a while, but it is finally deprecated in Visual C++ 2005.
Another case of this happening are the floating-point switches. In the past,
Visual C++ used the
Op switch to limit the freedom the optimizer
had with floating-point code generation. Now that Visual C++ 2005 has much more
granular control over floating-point with the
Op was no
longer necessary. Thus it was removed from Visual C++ 2005.
Switches that were obsolete
Some switches have just been around so long that they cannot possibly be useful
anymore. One such switch is the
G3 switch, as favoring 386
processors with the latest version of Visual C++ isn't going to happen. Another
example is the
QIfdiv switch... processors that don't have the
Pentium division bug have long been dominant in the market. Not to mention, the
GM switch is no longer necessary, since enabling MMX instructions
in the compiler is no longer useful these days. A long forgotten switch from the
Windows perspective was
GD which enabled specific optimizations for
DLLs. It turns out those optimizations weren't limited to DLLs, and the switch
has been unnecessary for years.
Sometimes problems are over-designed and under-implemented. One example of this
nologo- switch. It's meant to force the product banner to
show. Different versions of the product ignore it though.
Some other bizarre switches are the
switches, which tell the compiler to consider unknown files as object files. It
would be better to pass these files to the linker rather than the compiler,
especially if compiling with the
c switch. These are deprecated in
Visual C++ 2005.
Another switch that is both bizarre and wrong is one that we should have
gotten rid of years ago. The switch remained undocumented for a reason
– only "bozos" would use it. In fact the name of the
switch meant bozo alignment, which kept the alignment of local variables the
same as if it had been declared in a class; basically preventing the
optimizer from reordering local variables. Fortunately, we were able to correct
all the code that used bozo alignment and removed the switch from Visual C++
The single-threaded CRT
Reducing the test matrix is really important to Visual C++. In the past, it has
taken more than six weeks to complete a full test pass. In Visual C++ 2005, we
haven't really made huge cuts to functionality (i.e. the test matrix hasn't
gotten any smaller), but we've made a few attempts. One such attempt is removing
the single-threaded CRT. As a result, the
switches needed to be removed.
The output of
Mostly command-line users would be impacted by the results of
cl /?, but they're
more likely to use that to guide switch selection. Many of the changes I wanted
to see happen in the help results didn't actually happen. It's too bad, but I've
been busy driving the language design effort. First, I wanted to make the
results shorter and leave a more complete listing for the official
documentation. That would at least leave the really necessary switches (and
better defaults) for command-line users.
Second, I wanted to fix some of the text. If you read the description of the
GA switch, it says "optimize for Windows Application". Who wouldn't want to do
that? After all, Visual C++ only targets Windows. Well, it actually impacts
thread-local storage and the switch is safe for EXEs and not DLLs. I would have
replaced the description with something like "generate
TLS accesses for EXEs".
In the long term, many people have suggested having more in depth help
available at the command-line. Now that Visual C++ has support for localizable
text on the command-line, that's a more likely reality. Much will depend on the
feedback customers gives us over the coming years.
I started writing this because the first comment I got on my last post asked
about the G5, G6, and G7 switches. I certainly ended up writing much more, and
the sad part is that I could write way more than I already have. The experience
of investigating compiler modes and implementing the changes that actually did
happen was a huge learning experience for me. I'm still learning about customer
experiences due to changes like this. As I learn more and the rest of the Visual
C++ team learns more, we'll make even better design decisions in upcoming
releases. So, if you do have feedback, send it my way. J