Comparing G++ and Intel Compilers and Vectorized Code 225
Nerval's Lobster writes "A compiler can take your C++ loops and create vectorized assembly code for you. It's obviously important that you RTFM and fully understand compiler options (especially since the defaults may not be what you want or think you're getting), but even then, do you trust that the compiler is generating the best code for you? Developer and editor Jeff Cogswell compares the g++ and Intel compilers when it comes to generating vectorized code, building off a previous test that examined the g++ compiler's vectorization abilities, and comes to some definite conclusions. 'The g++ compiler did well up against the Intel compiler,' he wrote. 'I was troubled by how different the generated assembly code was between the 4.7 and 4.8.1 compilers—not just with the vectorization but throughout the code.' Do you agree?"
Documentation is King (Score:3, Interesting)
For better or worse, I've always given the intel compiler the benefit of the doubt. They have access to documents that the GCC folks don't.
Re: (Score:3)
The place I work has lots of documents generated about decisions made, why those decisions were made, etc...
They are really helpful documents that save a bunch of time... if only people would read them 6 months later when they should.
Nah, people seem to ask for documentation third. Google first, co-workers second. Only until they run into a co-worker that says RTFM do they get to the third option of reading it :)
When the manual is tl;dr (Score:3)
Re:Documentation is King (Score:5, Informative)
Re: (Score:2, Flamebait)
I'm sorry, you lost the right to put on the whole "Oh poor little AMD is being abused by the big bad monopolist!" the day that AMD came out with Mantle and started leveraging it's 100% monopoly in the console market in a much much worse way than Intel ever did with its 70 - 80% "monopoly" in the desktop market.
Re: (Score:2)
Who is talking about AMD? We're talking about Intel's surreptitiously anti-competitive behavior as it applies to trusting the efficacy of their compiler - the target of that behavior is irrelevant to the conversation.
Mantle? (Score:2, Insightful)
Mantle is a good idea insofar as it should kick Microsoft and/or NVIDIA up the behind. We desperately need someone to cure us of the pain that is OpenGL and the lack of cross platform compatibility that is Direct 3D.
Obviously NVIDIA won't play ball with Mantle but I've got a feeling they might have to eventually given that some AAA games developers are going code a path for it. When it starts showing up how piss-poor our current high level layers are compared to what the metal can do, they'll have no choi
AMD has no 100% monopoly (Score:2)
the day that AMD came out with Mantle and started leveraging it's 100% monopoly in the console market
Among consoles that aren't discontinued or battery-powered, I count Xbox 360, PlayStation 3, Wii U, Xbox One, PlayStation 4, and OUYA. Of these, two have NVIDIA graphics: PlayStation 3 has RSX, and OUYA has the same Tegra 3 that's in the first-generation Nexus 7 tablet. The forthcoming iBuyPower Steam Machine also has NVIDIA graphics.
Re: (Score:3, Informative)
*Except the academic, evaluation and Linux-only non-commercial use versions, which could theoretically be downloaded by AMD employees, I guess.
Re: (Score:2)
So, uh, how do you expect Intel to know what kind of optimizations will work best on AMD CPUs?
Re: (Score:3, Insightful)
If the CPU reports it supports SSE2, and the compiler supports it, I expect it to bloody well use those instructions when told to, not silently produce fucking x87 garbage. Really rocket science apparently.
Re: (Score:2)
well they sort of should be forced to not provide intentionally shitty product to their customers, no?
I got no qualms about them doing that if they put it in big red letters on the packaging and installer and on the page where you buy/get the compiler ....
Re: (Score:2)
And certainly customers are allowed to scream about it all they like if they feel cheated. That behavior is also very clearly an anti-competitive act.
Re:Documentation is King (Score:5, Interesting)
Yep, they have access to some cool documents. It took a lot of work to document the fact that the intel compiler was actually crippling code if it was run on AMD processors. I mean, some suspicious, somewhat paranoid people suspected that intel was crippling code on AMD processors, but it took a good deal of work to actually demonstrate it.
That is just one of the many reasons I don't use Intel.
Re:Documentation is King (Score:4, Insightful)
GCC also works with many CPUs that Intel compiler does not. That includes x86 compatible chips from other vendors, as well as the advanced features in Intel chips that were originally introduced by competiting clones. So maybe Intel is nice, but that's irrelevant if you don't even use Intel hardware in your products.
If Intel really is basing their compiler off of secret architecture documents, then people should be able to deduce what's going on from looking at the generated assembler. Ie, find some goofy generated code that does not seem to make sense given public documents, get a benchmark to compare it, figure out there's a hidden feature, and then make use of it.
Re: (Score:2)
"If Intel really is basing their compiler off of secret architecture documents, then people should be able to deduce what's going on from looking at the generated assembler. Ie, find some goofy generated code that does not seem to make sense given public documents, get a benchmark to compare it, figure out there's a hidden feature, and then make use of it."
In theory, given enough highly skilled eyes checking these things often enough, that is exactly what would happen.
In reality, who is going to go to that
Not sure why it's troubling. (Score:5, Insightful)
I don't think it's troubling.
Firstly they beat on the optimizer a *lot* between major versions.
Secondly, the compiler does a lot of micro optimizations (e.g. the peephole optimizer) to choose between essentially equivalent snippets. If they change the information about the scheduling and other resources you'd expect that to change a lot.
Plus I think that quite a few intresting problems such as block ordering are NP-hard. If they change the parameters of their heuristic NP-hard solver, that will give very different outputs too.
So no, not that bothered, myself.
Re:Not sure why it's troubling. (Score:5, Informative)
Mod parent up +1 insightful.
Unless you suspect and are trying to debug a code generator error (one of the least pleasant/most difficult debugging experiences I've had), the base assertion that you should understand your compiler's code generation is at best unrealistic, and probably just dumb. Code generation is extremely complex, requiring deep knowledge of both this specific compiler's design and this specific computer's instruction set architecture, how the caches work, pre-fetching approaches, timing dependencies in instruction pipelines, etc, etc. If you do suspect a code generator error, you're best off hiring a compiler expert at least as a consultant, and be prepared for a long hard slog.
Maybe 30 years ago, for a PDP-8, you could assert that the C code you wrote had some semblance to the generated machine code. That hasn't been true for a very long time, and C++ is most definitely not C in this regard.
Re: (Score:2)
Was your program dealing with dates or tenses?
Re: (Score:2)
Could you give an example?
Re: (Score:2)
Here is an example in GCC: the optimizer assumes the SSE minps instruction is commutative. It isn't.
As a result you can get unexpected results depending on the optimizer mood when you call this instruction with a NaN and a non-NaN value.
Re:Not sure why it's troubling. (Score:4, Informative)
Re:Not sure why it's troubling. (Score:4, Interesting)
Explicit vectorization is indeed much more reliable than automatic vectorization, and it will always deliver better performance.
Interestingly, there seems to be quite a few abstraction layer libraries for SIMD. There are also at least Boost.SIMD (part of NT2 [1]) and Vc [2].
Several array-handling libraries (NT2 [1], Eigen [3]) also a leverage SIMD explicitly.
Alternatively there are plenty of languages based on C with explicit SIMD programming, like the Intel SPMD Compiler [4].
If you're interested in SIMD, there is also apparently a workshop being held soon on this subject in Orlando [5].
[1] https://github.com/MetaScale/nt2 [github.com]
[2] http://code.compeng.uni-frankfurt.de/projects/vc/ [uni-frankfurt.de]
[3] http://eigen.tuxfamily.org/index.php?title=Main_Page [tuxfamily.org]
[4] http://ispc.github.io/ [github.io]
[5] https://sites.google.com/site/wpmvp2014/ [google.com]
Very different code (Score:4, Interesting)
I have worked on a couple of projects that compiled and ran perfectly with GCC 4.6 and 4.7. They no longer run when compiled with the latest versions of GCC. No warnings, no errors during compilation, they simply crash when run. It's the same source code, so something has changed. The same code, when compiled with multiple versions of Clang, runs perfectly. The GCC developers are doing something different and it is causing problems. Now it may be that a very well hidden bug is lurking in the code and the latest GCC is exposing that in some way, but this code worked perfectly for years under older versions of the compiler so it's been a nasty surprise.
Re:Very different code (Score:5, Insightful)
Unfortunately, that's not unique to GCC. I've seen this happen with several different compliers for different programming languages over the years. Worse, I've seen it with the same compiler, but different Optimizer settings.
In one case, our system didn't work (segfaulted) with the optimizer engaged, and didn't meet timing requirements without the optimizer. And the problem wasn't in our code, it was in a commercial product we bought. The compiler vendor, the commercial product vendor (and the developer of that product, not the same company as we bought it from) and our own people spent a year pointing fingers at each other. No one wanted to (a) release source code and then (b) spend the time stepping through things at the instruction level to figure out what was going on.
And the lesson I learned from this: Any commercial product for which you don't have access to source code is an integration and performance risk.
Re: (Score:3)
And trying to do it all yourself is a risk of never getting to market.
Re:Very different code (Score:5, Insightful)
Well, in part that depends on your market. Most of my work has been in military systems or air traffic systems, where the cost of failure >> lost opportunity cost. That's a point a lot of people forget; not all markets (and therefore the risk calculations for bugs, etc) are created equal.
Re: (Score:2)
Mark parent -1 troll.
That comment just demonstrates you don't know much about these kinds of markets.
Re: (Score:3)
You don't have to maintain the compiler yourself. You just need to have source code to it, and a compiler that compiles it, for the life of your project. That way, if a newer version of the compiler breaks your project, as the original poster complained of, you always have a working compiler for the life of your project. Your compiler may not get any additional improvements. But having it work vs not work is much more importan
Re: (Score:2)
Any commercial product for which you don't have access to source code is an integration and performance risk.
So true, I've run into the same problem. It doesn't mean you need to only use GPL, but you should try to get the source code when you sign the contract to use the product (you're probably paying enough, anyway).
Re: (Score:2)
Yup, and the commercial product with paid support does not actually get you a fix; in fact they may often tell you that you can upgrade to a later version if you purchase it.
Re: (Score:2)
That's why you don't buy software without support.
With support, they'd be contractually obliged to debug it.
Re: (Score:2)
We bought support. But to get support from organization X, that organization has to first admit it is -their problem-. Please re-read my post to see that no organization wanted to take ownership of the problem.
Re: (Score:2)
Depending on the support contract you negotiated with them, they shouldn't need to admit it.
I recommend you tell management to improve their legal department.
Re: (Score:2)
Re: (Score:2)
This is typically very rare, because the object modules usually have to conform to an ABI. If unoptimized and optimized modules can not work together then something is breaking the ABI.
Granted there are sometimes special cases. Ie, on an AVR (8-bit chip) sometimes I want to reserve a global variable in a fixed register, but that requires all modules be compiled with the same flag. But this is explicitly a special case, where the compiler is being told to break the rules.
Creating your own ABI (Score:2)
sometimes I want to reserve a global variable in a fixed register, but that requires all modules be compiled with the same flag.
That isn't "breaking the rules" as much as creating your own ABI. Classic Mac OS on 68K used to do this, where register A5 was typically reserved as a pointer to the program's global variable segment because the Mac OS ABI used position-independent code.
Re: (Score:2)
Re: (Score:2)
Doesn't seem that hard to me. The compiler is making certain assumptions when compiling code with a given set of optimizations. It shouldn't be difficult to determine when those assumptions may affect, for example, memory alignment of data being passed between modules (I'm just guessing that's one of the common culprits). It may not be easy to eliminate those assumptions entirely, but it should be trivial to make a notation in the object file indicating the potentially incompatible assumptions made and l
Re: (Score:2)
"having source code wouldn't have changed anything" Disagree. Particularly at interfaces (e.g. cross-language/cross-compiler calls, APIs to COTS products, etc), sometimes you need to see inside the product to figure out what's failing at the interface. It's nice to talk about omniscient knowledge on the part of product developers, API specifiers (who might not be the same as the API implementers), and customers/users of that API for that product. Our specification techniques are by no means rigorous e
Re: (Score:2)
it may be that a very well hidden bug is lurking in the code and the latest GCC is exposing that in some way
I have run into this situation. The code actually depended upon a bug in the older gcc versions. When that bug was fixed, the code stopped working. In some cases, the compile failed, in others, it crashed at runtime.
Specifically, this was around gcc version 2.7, and the bug was this: for (int i=0; i < SIZE; i++) { ... } for (i=0; .... The variable "i" should be out of scope for the 2nd loop and cause an error during compilation, but gcc didn't catch it. gcc version 2.95 caught it. I forget if tha
Re:Very different code (Score:4, Informative)
Re:Very different code (Score:5, Informative)
Re: (Score:2)
I used to try to do that, but there are several problems. First, it's non-portable as hell. Second, have you ever tried turning on all the warning options in gcc (and some other compilers)? I'm not sure it's possible to write 10 lines of code that won't generate at least one warning.
Re:Very different code (Score:4, Insightful)
Re:Very different code (Score:4, Informative)
There is a reason for warnings -- it's because you're doing something wrong.
Uh, no. It's because you're doing something that may be wrong. If it was wrong, the compiler would given an error, not a warning.
'if ( a = b )' for example. The compiler warns because you probably meant 'if ( a == b )'. But maybe you didn't.
There's little reason to write such C code on a modern quad-core 3GHz CPU which spends 90% of its time idle and where the compiler will probably generate the same machine code anyway, but that doesn't make it wrong.
Re: (Score:3)
1. Someone else won't necessarily know that this was intended unless you have a comment telling them. That comment would take up just as much space as putting an 'a = b;' line before the 'if (a)' line.
2. If your comp
Re: (Score:2)
"'if ( a = b )' for example. The compiler warns because you probably meant 'if ( a == b )'. But maybe you didn't."
If you did not, then you should have written something like a=b;if b {... instead. It may be technically legal code but it's very bad and it should be flagged and warned, you are just being cute at the expense of readability and even you probably wont remember what the heck you did there 6 months later when you look at the code again.
try a more complicated version (Score:2)
if (a && b=f(a) && c=g(b)) {
do stuff with a and b and c
}
If you convert that into the other format then you need to add something like six lines of code and two levels of nested if statements.
Re: (Score:2)
Right, but would your server utilization go from 80% to 90% if you wrote it as two lines?
a = b;
// yadda
if (a) {
}
Unless you have an incredibly crappy compiler, the two will generate identical code, but this second version won't give a warning.
Re: (Score:2)
if ( ( a == b ) ) {
}
Now it's no longer an assignment where a boolean expression is expected, it's an expression where a boolean expression is expected.
Re: (Score:2)
Re:Very different code (Score:4, Informative)
Sometimes warnings are false positives as well. Especially when turning warning levels up high they will warn about things that may be indicators of a bug or typo but which actually aren't problems, or in some cases are even intentional. Such as unused variables or parameters; is that a bug or a stylistic choice to not litter the code with extra #ifdef? An unused parameter in general seems an odd thing to complain about, usually the parameter list is fixed in an API or design document whether or not the actual implementation needs all the parameters.
Re: (Score:2)
It's not that hard really, if you start from scratch this way. Where it falls down is trying to do this with a large existing product. Also C is trickier here than C++ because C++ has more rigorous rules to begin with.
For me the hard part with turning all warnings on is not the code developed locally for the project, but the third party libraries that come with source code. Those libraries probably generate 95% of all warnings and red flags in static analysis.
Re: (Score:2)
Re: (Score:2)
This is a good idea. However sometimes are warnings that should be ignored. Ie, if a function takes 4 parameters in the declaration but the implementation uses only 3 paramters, then GCC will issue a warning. But the API says you need 4 parms so you can't just remove it. So the solution is either to turn off that specific warning (usually means it's off in all files) or add a dummy use case for the parameter, etc, even though there is no bug in the code.
Re: (Score:2)
Re: (Score:2)
Sure there is. Your declaration doesn't match your definition. Bad. Fix it.
I think you missed something there. The declaration and definition would match, but he doesn't use one of the parameters.
While there are quite a few ways to "fix" this; I'm honestly not sure what the best is.
Re: (Score:2)
Re: (Score:2)
Actually it's not that portable, though that's only with some compilers.
Bigger problem is that the parameter actually is used, but only in some builds, because there's an #ifdef in the function. This also happens sometimes with variables, because in C you have to declare the variable up at the top to be portable, whereas the code that uses it may be at the bottom of the function, so the programmers often just use the #ifdef at the point of use without an #ifdef at the declaration.
I had started using things
Re: (Score:2)
I still recommend compiling clean with -Wall but it does mean that t
Re: (Score:2)
GCC warns about unrecognized pragmas as well. So to use that new pragma you either need to #ifdef it, or make sure the entire programming team, QA, and build, all swap over to the new compiler at the same time.
Of course adding this stuff is not that bad when you are writing code from scratch or on a new project. It makes a great deal of sense to make sure all new projects begin life with warnings turned up to the max. However when the project is many years old and you've got third party libraries to comp
Re: (Score:2)
You should put a high warning level and pragmas for the compiler you use to DEVELOP, so that you can see well every new warning and decide if you want to change your code or shut up the compiler.
That's fine if you use gcc or clang, because they do a good job of analyzing the code to decide if a warning is warranted. I like to use -Wall, and sometimes a few others. It's a whole different story if you use proprietary compilers for embedded work, many of which spit out warnings for no reason or fail to spit out warning when they should.
Re: (Score:2)
These days I compile everything with both clang and gcc, and I fix all the warnings
I do the same thing when I have the pleasure of using gcc. One of the things I like about it is the quality of the warnings. Other compilers I have to use, typically proprietary compilers for embedded work, are not so good.
If you have warnings you are doing something wrong, period.
Then they should be errors. Yes, I know about -Werror on gcc but the problem is that there is no standard for warnings. What's clean on one compiler gives warnings on another, and vice versa.
The real bottom line is that if you want to do that level of anal retentive checking (and I'm a bi
Re: (Score:2)
Not at all. There's a very big difference between "There was a bug in the compiler which let X slip through until update Y" and "Update Y adheres to new language specifications that make X illegal", even if they look quite similar to a programmer who doesn't follow developments in the language. Can't remember for sure which camp this fell in, but I do remember dealing with the issues for years when moving between compilers that adhered to the old and new standard.
IIRC under the old standard doing it the n
Re: (Score:3)
Quite true about C++. I've used it to good effect in conjunction with "active" objects whose creation and destruction can encapsulate much of their functionality - things like thread locks, at the simple end.
Re: (Score:2)
Stop. You are making me feel old, I remember writing code like that. (Which compiled)
Re: (Score:2)
The samples of assembler made me feel old! I'm familiar with x86 assembler, and the variations on MOV that go back to the 8086 (things like MOVSB, MOVSW), but this VMOVUPD was totally new to me. But then I have never looked at SSE, or even MMX.
Re: (Score:2)
It can as well be that you have a subtile memory bug which never really triggered when compile with the old compiler.
E.g. the latest bug like this, which I encountered.
something like:
char[10] data;
read a file with 10 bites into "data". Assume it is a 0 terminated string.
Surprisingly that always worked as malloc() handed out chunks devidable by 4, zero initialized. So behind the 10 bytes where 2 more zero bytes.
Switching to another compiler (more correctly clibrary) made that code crash, but it worked for ye
Re: (Score:2)
Alignment tends to have problems here too. Small changes in compiled code will place data differently and as soon as it's unaligned the bugs pop up (this really freaks out some programmers who previously only used Wintel and never heard of alignment before). Similarly, buffer overflows as you describe may be perfectly fine until a change in the compiler occurs; it's particularly nasty to track down if it clobbers something on the stack so that the crash doesn't occur until the calling function returns.
Re: (Score:2)
And malloc definitel does not zero initialize by design!
But, at least early in the program's execution before you start reallocating freed memory, there's a good chance the memory malloc() returns will be zero-filled.
I seem to remember that debug builds on Windows fill allocated memory with a known pattern for just this reason? Or maybe that was a special hacked version of malloc() that we were using.
Re: (Score:2)
http://harmful.cat-v.org/software/GCC [cat-v.org]
Re:Very different code (Score:4, Insightful)
Re: (Score:2)
I wouldn't say extremely rare. It depends entirely of what you are doing.
Some parts of the compiler are more stable than others.
Advanced C++ and gcc-specific extensions are two things that can break from time to time. Combine the two together, and running into bugs isn't so rare.
Re: (Score:2)
Re: (Score:2)
Well, first off that's clearly bad code as you point out in the comments - it will result in undefined behavior if baz in null, a situation that's clearly being expected. Granted, it's hard to imagine a scenario outside an extremely naive compiler where that particular undefined behavior is anything other than harmless - well other than this one, where it causes undefined compile-time behavior. I would agree that a warning in such a situation would be nice, in a "we all make stupid mistakes sometimes" kind
Re: (Score:3)
Most likely, you were just invoking undefined behaviour.
GCC 4.8 has new optimizations tied to signed integer overflow, for example. a+b is the same in hardware regardless of whether the inputs are signed or not (assuming two's complement hardware), but to the compiler, that's not the case.
Re: (Score:3)
Easy 99% solution - treat all compiler warnings as errors. A warning means the compiler is having to guess at the proper interpretation of your code because you didn't make it completely unambiguous in the grammar of the language. A different compiler (or version) can thus be reasonably expected to guess differently, changing the behavior of your code.
Once I just changed just one line (Score:2)
News for nerds or not (Score:5, Informative)
Re: (Score:2)
Also notably absent were any performance benchmarks. Two pieces of code might look very different but perform identically, while two others that look very similar could have very different performance. In any case, you should be able to work back to an achieved FLOPS number, for example, to understand quantitatively what the compiler achieved. You might have the most vectorific code in existence, but if it's a cache pig, it'll perform like a Ferrari stuck in mud.
Re: (Score:2)
Why are you counting in FLOPS in the first place? Use a real unit.
Cache is independent from vectorization. While both affect the performance of the code, when evaluating the performance of vectorization on its own only how many cycles the computation would take if all data were in L1 cache is considered.
Re: (Score:2)
Considering that Cogswell's previous works include a bunch of completely useless compiler benchmarks that tell you how fast the *compiler* produced the code, and now how fast the resulting code was... I don't think we should be surprised that he's produced another useless article.
When I use a compiler, I don't really care what the assembly it produces look like, I care about how it performs.
Re: (Score:2)
sudo mod parent up
Re: (Score:2)
Vectorized factorials! (Score:4, Interesting)
One amusing thing I discovered is that GCC 4.8.0 will actually unroll and vectorize this simple factorial function: [spatula-city.org] Just look at that output! [spatula-city.org]
Re:Vectorized factorials! (Score:5, Funny)
Here is how I do a factorial function. No recursion, no loops, no vectorization needed. It's in Java. Converting this basic idea to C is left as an exercise for advanced readers.
static public long factorial( int n ) {
switch( n ) {
case 0:
case 1: return 1L;
case 2: return 2L;
case 3: return 6L;
. . . cases 4 to 18 omitted to bypass slashdot filters . . .
case 19: return 121645100408832000L;
case 20: return 2432902008176640000L;
}
return 0L;
}
Re: (Score:3, Informative)
Re: (Score:2)
Re: (Score:2)
Is that some sort of joke? Surely you can tell this is not the optimal assembly code at all.
Why is this still a topic? (Score:4, Interesting)
This is 2013 (almost 2014!) why are we talking about vectorization? Why don't people write code in vector notation in the first place anyway? If Matlab and Fortran could implement this 25 years ago, I am sure we are ready to move on now...
Re: (Score:2)
Re: (Score:2)
That's the biggest complaint you have about Fortran, some minor syntactic detail? If that's it's biggest problem, then Fortran must be the greatest language ever created.
Re: (Score:2)
Because the slowest part of the computer is memory, and vector notation leads to more cache misses.
Trust but verify (Score:3)
...do you trust that the compiler is generating the best code for you?,,,
Trust, but verify.
.
I come from the days when it was the programmer, not the compiler, that optimized the code. So nowadays, I let the compiler do its thing, but I do a lot of double-checking of the generated code.
Re:yuo Fai7 It (Score:4, Funny)
Re: (Score:3, Informative)
I write code in Machine Code with a bootable hex editor (446 bytes, fits in a HDD boot sector). It's the easiest way to bootstrap an OS from scratch now that MoBos don't have boot from serial port anymore...
Here, run it in a VM: "qemu-system-i386 hexboot.img [vortexcortex.com]", if you want.
Or, "dd if=hexboot.img of=/dev/sda bs=1 count=446 conv=notrunc", if you want to preserve the partition table on a bootable drive.
Arrows,PgUp,PgDn,Home,End = navigate; Tab = ASCII/Hex, Esc = jump to segment under cursor, F8 = Run code at
Re: (Score:2)
Actually, no. Computers are not getting faster.
Microprocessors stopped getting faster a few years ago, now we just get more of them. Supercomputers have mostly reached the limits of scalability, so there is a limit to that too.
Re: (Score:3)
Hey genius, try removing the 'as' command and then run gcc. Let us know how well the compiler works without an assembler.
Object code formats (Score:2)
A compiler going to an assembler today is LAME.
How so? A tool should do one thing well. What an assembler does well is generate relocatable object code in a given format. If you're targeting two platforms, one of which uses ELF and the other COFF or whatever, one could use the same compiler to target both along with two different assemblers, one for each object code format.