Comparing C++17 Compilers and their generated optimized assembly [duplicate] - c++

Lately I'm diving into optimizing my C++ code and as such started to play around with the compiler explorer. Since i am mainly developing on windows with Visual Studio i used the msvc compiler.
At some point msvc got out of hand. after some fiddling around i could narrow it down to the iostream header, that's supposed to be preferred for I/O (SL.io.3).
#include <iostream>
int main() {
std::cout << "Hello World!\n";
return 0;
}
While gcc or clang's total output (main + a static initializer that calls some ios_base init functions) totals about 20 lines of assembly (after the Godbolt compiler explorer filters out directives and comments).
MSVC explodes it into 4000. Most of those lines are separate functions; MSVC's definition of main itself is 7 instructions vs. 8 for gcc/clang. (gcc/clang using GNU/Linux libstdc++ pass an extra length arg to the cout operator overload function, not just 2 pointers like MSVC does when using its own C++ library.)
If i use something like puts instead, MSVC's total output is reasonably compact and comparable to gcc/clang, like here.
Can someone kindly explain to me what is happening here, what im doing wrong or point me in the right direction?
Why are MSVC asm listings so bloated for simple functions using C++ libraries?

This may not be a complete answer, but I think I can explain much of the differences.
Much of the standard library (e.g., iostreams) is template heavy code. I believe the Microsoft compiler generates more template instantiations and relies on the linker to remove unnecessary ones. I think that's an effect of the different strategies the Windows linker uses versus most Posix ones, but it might also be a result of simply using a different standard library implementation.
If you specify /MD, which tells the compiler you intend to use the DLL version of the standard library, the generated code drops from 4000+ lines to fewer than 500 lines. I don't know precisely why that's the case. Perhaps MSVC knows the DLL library has all the necessary template instantiations while the static library depends on template instantiation from the compiler.
You can elicit an incremental improvement by handling only C++ exceptions (with /EHs). By default, the compiler will generate code that handles asynchronous system exceptions as well. And while your hello-world sample doesn't explicitly use exceptions, parts of the standard library probably do. At this point, it looks like a lot of the additional lines are setting up stack unwinding tables and calling destructors.
A lot of the remaining excess in the MSVC version looks like it exists to unwind the stack while calling destructors, so the exception handling model may be different.
I thought Compiler Explorer had a "clang-cl" option in the past but I don't see it now. clang-cl, generally speaking, is a command driver that interprets cl.exe options and adjusts default options to make clang produce code that's binary ABI compatible with Microsoft code. It would be interesting to see if it generates code like regular clang or whether it ends up emitting code more like MSVC.

Related

GCC 4.6.2 inlining behavior

-- snipped from chat.so --
I am stuck with gcc 4.6.2 on a certain project and after profiling with intel VTune
i noticed that very insignificant functions were not being inlined (or at least showed up under hotspots, which I assumed meant a failed inline)
an example function is a reinterpret cast, 2 numeric additions, and a ternary statement
i BELIEVE these are being inlined in Windows, but due to the profiling, think they are not being inlined in linux under gcc 4.6.2
I am attempting to get an ICC build working in linux (works in windows), but that'll take a little time
until then, does anyone know if GCC 4.6.2 is that different from VS2010 in terms of relatively simple compiler optimizations? I've turned on -O3 in GCC
what led me to this is that this is a rewrite of a significant section of code, and on Windows, the performance is approximately equal or a little slower, while on Linux it is at least 2x as slow.
The most informative answer would help me understand the steps required to verify inlining across platforms and how best to approach this situation as I understand these things are extremely situation-specific.
EDIT: Also, assuming that business-specific reasons force me to stick with GCC 4.6.2, what can I do about this without rewriting the code to make it less maintainable?
Thanks!
First the super-obvious for completeness: Are you absolutely sure that all the files doing the probably non-inlined calls were compiled with -O3?
The gcc and VS compiler and tool chains are sufficiently different that it wouldn't surprise me at all if their optimizers behaved rather differently.
Next let me observe that the ternary operator can be very deceiving. Ternary operators are almost certainly going to create a branch and potentially constructor calls, conversions, etc. Don't assume that just because it's a terse operator in C++ the compiler will be able generate a tiny amount of code for it. This could potentially inhibit the compiler from optmizing it. In fact, you could try reworking the ternary code into a normal if statement and see if that helps your performance at all.
Then once you've moved on to further diagnostics, an easy thing to try is to use strings <binary> | grep function and see if the function name shows up in the binary at all. If it doesn't then it's definitely being inlined (although even if it shows up it could be strictly debug information and not actual code). There are other tools such as nm, readelf, elfdump, and dump that can introspect binaries for symbols as well. You would need to see which tools are available on your platform and then try to use them to find the function(s) in question.
Another idea is to load the compiled binary into gdb, and ask it to disassemble the code at the file and line at the point where the function call is made. Then you can read the disassembly code to see what the compiler did. Most of the code should actually be fairly obvious. You will likely see something like a call instruction if an actual function call was made.

Change from C compiler to C++ compiler causes compiler errors for MSP430 firmware

I am trying to compile a tried and true piece of firmware for the Texas Instruments MSP430 micro-controller with a new compiler (a C++ compiler, under Visual Studio using VisualGDB).
All prior versions of this firmware were compiled with a C compiler, with zero errors and zero warnings. The change from a C compiler to a C++ compiler must be the reason for my problem. (The code compiled with the C compiler has been running without problems for five years.)
Given this code snippet:
#include <msp430x14x.h>
WDTCTL = WDTPW+WDTHOLD;
I get this error:
error: 'WDTCTL' does not name a type c:\msptest\LedBlink\LedBlink.cpp
WDTCLT is declared in msp430x14x.h.
Can someone please point me in the right direction?
Two things are possible:
msp430x14x.h provided with each compiler are different - there is no standard form for such a header and each vendor may define them differently.
You are using the header from one compiler with another and it uses incompatible compiler extensions or syntax.
There is no doubt a great deal of compiler specific or "macro magic" going on here - what you need to look at is exactly how these macros are expanded by the pre-processor. The compiler is complaining about the expanded code, not the source you have posted.
The pre-processor has been used here to create syntax that would not otherwise be valid C or C++ so without consideration of the expansion, not much can be determined. And since each compiler may supply a different msp430x14x.h, without knowing exactly what C and what C++ compiler were used, not a lot can be said on that either. Remember that the full expansion must be considered - so if a macro is defined in terms of other macros, they too must be expanded. GCC has a n option top output the pre-processor output, or you can run the pre-processor separately directly (the executable is "cpp").
The problem is that WDTCTL is defined using sfrw, and although not shown in your code, sfrw is defined in iomacros.h as an inline asm statement. Clearly inline asm is going to be processor dependent and what works for msp430 won't work in visual studio.
Thank you to all that responded. The responses are helpful beyond the question that I asked.
I have found, and corrected, the problem. The problem was, I must admit, a careless error on my part. I cut and pasted code from a completed C program into a test program, omitting
int main()
Thus, the assignment statements that the compiler complained about were in the incorrect scope of the source code.
I posted my question prematurely.
That said, your answers are most helpful in making a decision to move ahead with using the GCC compiler (with Visual Studio as the IDE), or using the same compiler that I used for all previous versions of this firmware. I learned much from the set of answers you provided.
Thanks!

Are there conclusive studies/experiments on C compilation using a C++ compiler?

I've seen a lot of arguments over the general performance of C code compiled with a C++ compiler -- I'm curious as to whether there are any solid experimental studies buried beneath all the anecdotal flame wars you find in web searches. I'm particularly interested in the GCC suite, but any data points would be interesting. (Comparing the assembly of "Hello, World!" is not as robust as I'd like. :-)
I'm generally assuming you use the "embedded style" flags -- no exceptions or RTTI. I also wouldn't mind knowing if there are studies on the compilation time itself. TIA!
Adding a datapoint (or at least an anecdote):
We were recently writing a math library for a small embedded-like target, and started writing it in C. About halfway through the project, we switched some of the files to C++, largely in order to use templates for some of the functions where we'd otherwise be writing many nearly-identical pieces of code (or else embedding 40-line functions in preprocessor macros).
At the point where we started switching over, we had a very careful look at the generated assembly code (using GCC) on a number of the functions, and confirmed that it was in fact essentially identical whether the file was compiled as C or C++ -- where by "essentially identical" I mean the differences were in things like symbol names and the stuff at the beginning and end of the assembly file; the actual instructions in the middle of the functions were exactly identical.
Sorry that I don't have a more solid answer.
Edit to add, 2013-03-24: Recently I came across an article where Rusty Russell compared performance on GCC compiled with a C compiler and compiled with a C++ compiler, in response to the recent switch to compiling GCC as C++: http://rusty.ozlabs.org/?p=330. The conclusions are interesting: The version compiled with a C++ compiler was very slightly slower; the difference was about 0.3%. However, that was entirely explained by load time differences caused by larger debug info; when he stripped the binaries and removed the debug info, the differences were less than 0.1% -- i.e., essentially indistinguishable from measurement noise.
I don't know of any studies off-hand, but given the C++ philosophy that you don't pay the price for features you don't use, I doubt there'd be any significant difference between compiling C code with the C compiler and with the C++ compiler.
I don't know of any studies and I doubt that anyone will spend the time to do them. Basically, when compiling with a C++ compiler, the code has the same semantic as when compiling with a C compiler, so it's down to optimization and code generation. But IMO these are much too much compiler-specifc in order to allow any general statements about C vs. C++.
What you mainly gain when you compile C code with a C++ compiler is a much stricter checking (function declarations etc.). IMO this would make compiling C code with a C++ compiler quite attractive. But note that, if you have a large C code base that's never run through a C++ compiler, you're likely facing a very steep up-hill battle until the code compiles clean enough to be able to see any meaningful warnings.
The GCC project is currently under a transition from C to C++ - that is, GCC may be implemented in C++ in the future, it is currently written in C. The next release of GCC will be written in the subset of C which is also valid C++.
Some performance tests were performed on g++ vs gcc, on GCC's codebase. They compared the "bootstrap" time, which means compiling gcc with the sysmem compiler, then compiling it with the resulting compiler, then repeating and checking the results are the same.
Summary: Using g++ was 20% slower. The compiler versions were slightly different, but it was thought that this wouldn't cause there 20% difference.
Note that this measures different programs, gcc vs g++, which although they mostly use the same code, have different front-ends.
I've not tried it from a performance standpoint, but I think compiling C applications with the C++ compiler is a good idea, as it will prevent you from doing "naughty" things such as using functions not declared.
However, the output won't be the same - at the very least, you'll get different symbols, which will render it (mostly) unlinkable with code from the C compiler.
So I think what you really mean is "Is it ok from a performance standpoint, to write C++ code which is very C-like and compile it with the C++ compiler" ?
You would also have to not be using some C99 things such as bool_t which C++ doesn't support on the grounds of having its own ones.
Don't do it if the code has not been designed for. The same valid language constructs can lead to different behavior if interpreted as C or as C++. You would potentially introduce very difficult to understand bugs. Less problematic but still a maintainability nightmare; some C constructs (especially from C99) are not valid in C++.
In the past I have done things like look at the size of the binary which for C++ was huge, that doesnt mean they simply linked in a bunch of unusued libraries. The easiest might be to use gcc -S myprog.cpp vs gcc -S myprog.c and diff the assembler output.

What are the practical differences between C compilers on Windows?

A program written in Visual C/C++ 2005/2008 might not compile with another compiler such as GNU C/C++ or vice-versa. For example when trying to reuse code, which uses windows.h, written for a particular compiler with another, what are the differences to be aware of?
Is there any information about how to produce code which is compatible with either one compiler or another e.g. with either GC/C++ or MSVC/C++? What problems will attempting to do this cause?
What about other compilers, such as LCC and Digital Mars?
The first thing to do when trying to compile code written for MSVC to other compilers is to compile it with Microsoft-extensions switched off. (Use the /Za flag, I think). That will smoke out lots of things which GCC and other compilers will complain about.
The next step is to make sure that Windows-specific APIs (MFC, Win32, etc.) are isolated in Windows-specific files, effectively partioning your code into "generic" and "windows-specific" modules.
Remember the argument that if you want your web page to work on different browsers, then you should write standards-compliant HTML?
Same goes for compilers.
At the language level, if your code compiles without warnings on GCC with -std=c89 (or -std=c++98 for C++), -pedantic -Wall, plus -Wextra if you're feeling brave, and as long as you haven't used any of the more blatant GNU extensions permitted by -pedantic (which are hard to do accidentally) then it has a good chance of working on most C89 compilers. C++ is a bit less certain, as you're potentially relying on how complete the target compiler's support is for the standard.
Writing correct C89 is somewhat restrictive (no // comments, declarations must precede statements in a block, no inline keyword, no stdint.h and hence no 64bit types, etc), but it's not too bad once you get used to it. If all you care about is GCC and MSVC, you can switch on some language features you know MSVC has. Otherwise you can write little "language abstraction" headers of your own. For instance, one which defines "inline" as "inline" on GCC and MSVC/C++, but "__inline" for MSVC/C. Or a MSVC stdint.h is easy enough to find or write.
I've written portable code successfully in the past - I was working mostly on one platform for a particular product, using GCC. I also wrote code that was for use on all platforms, including Windows XP and Mobile. I never compiled it for those platforms prior to running a "test build" on the build server, and I very rarely had any problems. I think I might have written bad code that triggered the 64bit compatibility warning once or twice.
The Windows programmers going the other way caused the occasional problem, mostly because their compiler was less pedantic than ours, so we saw warnings they didn't, rather than things that GCC didn't support at all. But fixing the warnings meant that when the code was later used on systems with more primitive compilers, it still worked.
At the library level, it's much more difficult. If you #include and use Windows APIs via windows.h, then obviously it's not going to work on linux, and the same if you use KDE with GCC and then try to compile with MSVC.
Strictly speaking that's a platform issue, not a compiler issue, but it amounts to the same thing. If you want to write portable code, you need an OS abstraction API, like POSIX (or a subset thereof) that all your targets support, and you need to be thinking "portable" when you write it in the first place. Taking code which makes heavy use of windows-specific APIs, and trying to get it to work on GCC/linux, is basically a complete rewrite AFIAK. You might be better off with WINE than trying to re-compile it.
You're mixing up "compilers" and "OSs". <windows.h> is not something that MSVC C compiler brings to the table: it's C-specific embodiment of Windows API. You can get it independently from Visual Studio. Any other C compiler on Windows is likely to provide it. On the Linux side, for example, you have <unistd.h>, <pthereads.h> and others. They are not an essential part of GCC, and any other compiler that compiles for Linux would provide them.
So you need to answer two different questions: how can I code C in such a way that any compiler accepts it? And how do I hide my dependencies on OS?
As you can tell from the diverse answers, this topic is fairly involved. Bearing that in mind here are some of the issues I faced when recently porting some code to target three platforms (msvc 8/Windows, gcc 4.2/Linux, gcc 3.4/embedded ARM9 processor). It was originally only compiling under Visual Studio 2005.
a) Much code that's written on the Windows platforms uses types defined in windows.h. I've had to create a "windows_types.h" file with the following in it:
#ifndef _WIN32
typedef short INT16;
typedef unsigned short UINT16;
typedef int INT32;
typedef unsigned int UINT32;
typedef unsigned char UCHAR;
typedef unsigned long long UINT64;
typedef long long INT64;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef void * HANDLE;
typedef long LONG;
#endif
Ugly, but much easier than modifying the code that, previously, was only targeting Windows.
b) The typename keyword was not required in templated code to declare types. MSVC is lax in this regard (though I assume certain compiler switches would have generated a warning). Had to add it in a number of places.
c) Simple, but time-consuming: Windows is not case sensitive and many #included files were specified with incorrect case causing problems under Linux.
d) There was a fair chunk of code using the Windows API for many things. An example was for CRITICAL_SECTIONS and INTERLOCKED_INCREMENT. We used the boost libraries as much as possible to replace these issues but reworking code is time-consuming.
e) A lot of the code relied on headers being included in precompiled headers. We had issues with using pch on gcc3.4 so we had to ensure that all .h/cpp files correctly included all their dependencies (as they should have in the first place).
f) VS 2005 has two nasty bugs. auto_ptr's can be assigned to anything and temporary variables are allowed to be passed to reference parameters. Both fail to compile (thankfully!) under gcc but rework is required.
g) Bizarrely, we had template code that was trying to explicitly specialise class template functions. Not allowed. Again gcc refused, VS 2005 let it go. Easy enough to rework into regular overloads once the problem is understood.
h) Under VS 2005 std::exception is allowed to be constructed with a string. Not allowed under gcc or the standard. Rework your code to prefer to use one of the derived exception classes.
Hopefully that's the kind of information you were looking for!
Well this is a quite difficult question. Fact is that MSVC does not support the newest
C standard, about it's c++ compliance I can tell you anyything. Howerver "windows" C is understand by both MSVC and gcc you just can not hope for the other way. E.g if you use ANSI C99 features then you might have a hard time "porting" from gcc to MSVC.
But as long as you try the way MSVC-> gcc you chances will be better. The only point you have to be aware of is the libraries. Most of the libraries on Windows are supposed to work with MSVC and so you need some extra tools to make them accessible to gcc also.
LCC is a quite older system,which AFAIKT does not support much from ANSI C99, it also needs tools from MSVC to work properly. LCC is "just " a compiler.
lcc-win32 is a C Development system striving to be ANSI C99 compliant. It comes along with linker, IDE etc.
I can not tell about the state of Digital Mars implementation
Then there is also Pelles-C which is a fully fledged IDE also.
And we have hanging around OpenWatcom. Which once was quite a decent system but I can't tell how conformant it is.
All in all the "best" you can hope for the easier way from MSVC -> other system but it will probably be much worse the other way round.
Regards
Friedrich
vs2008 is a lot more standards compliant than 2005.
I have had more problems going the other way, especially the 'feature' of gcc that lets you allocate an array with a variable size at run time "int array[variable]" which is just pure evil.
A program written in Visual C/C++ 2005/2008 might not compile with another compiler such as GNU C/C++ or vice-versa.
This is true if you either (1) use some sort of extension available in one compiler but not another (the C++ standard, for instance requires the typename and template keywords in a few places but many compilers -- including Visual C++ don't enforce this; gcc used to not enforce this either, but changed in 3.4) or (2) use some standard compliant behavior implemented on one compiler but not another (right now the poster boy for this is exported templates, but only one or two compilers support this, and Visual C++ and gcc are not in that group).
For example when trying to reuse code, which uses windows.h, written for a particular compiler with another,
I've never seen a problem doing this. I have seen a problem using Microsoft's windows.h in gcc. But when I use gcc's windows.h in gcc and Microsoft's windows.h in Visual C++ I have access to all of the documented functions. That's the definitions of "implemented windows.h" after all.
what are the differences to be aware of?
The main one I've seen is people not knowing about the dependent template/typename thing mentioned above. I find it funny that a number of people think gcc is not smart enough to do what Visual C++ does, when in reality gcc had the feature first and then decided to remove it in the name of standards compliance.
In the near future you will run into problems using C++0x features. But both gcc and Visual C++ have implemented the easier things in that standard.

Mixing C/C++ Libraries

Is it possible for gcc to link against a library that was created with Visual C++? If so, are there any conflicts/problems that might arise from doing so?
Some of the comments in the answers here are slightly too generalistic.
Whilst no, in the specific case mentioned gcc binaries won't link with a VC++ library (AFAIK). The actual means of interlinking code/libraries is a question of the ABI standard being used.
An increasingly common standard in the embedded world is the EABI (or ARM ABI) standard (based on work done during Itanium development http://www.codesourcery.com/cxx-abi/). If compilers are EABI compliant they can produce executables and libraries which will work with each other. An example of multiple toolchains working together is ARM's RVCT compiler which produces binaries which will work with GCC ARM ABI binaries.
(The code sourcery link is down at the moment but can be google cached)
I would guess not. Usually c++ compilers have quite different methods of name-mangling which means that the linkers will fail to find the correct symbols. This is a good thing by the way, because C++ compilers are allowed by the standard to have much greater levels of incompatibility than just this that will cause your program to crash, die, eat puppies and smear paint all over the wall.
Usual schemes to work around this usually involve language independent techniques like COM or CORBA. A simpler sanctified method is to use C "wrappers" around your C++ code.
It is not possible. It's usually not even possible to link libraries produced by different versions of the same compiler.
No. Plain and simple :-)
Yes, if you make it a dynamic link and make the interface c-style. lib.exe will generate import libraries which are compatible with the gcc toolchain.
That will resolve your linking problems. However that is just the start of the problem.
Your larger problems will be things like exceptions, and memory allocation.
You must ensure that no exception cross from VC++ to gcc code, there are no guarantees of compatibility.
Every object from the VC++ library will need to live on the heap because:
Do not mix gcc new/delete with anything from VC++, bad things will happen. This goes for object construction on the stack too. However, if you make an interface like create_some_obj()/delete_some_obj() you do not end up using gcc new to construct VC++ objects. Maybe make a small handler object that handles construction and destruction. This way you preserve RAII, but still use the c-interface for the true interface.
Calling convention must be correct. In VC++ there is cdecl and stdcall. If gcc tried to call an imported function with the wrong calling type, bad things will happen.
The bottom line is keep a simple interface that is ANSI C compliant, and you should be fine. The fact that crazy C++ goes on behind is okay, as long as it is contained.
Oh and make sure all the code is re-entrant, or you risk opening a whole nother can-o-worms.