Phusion white papers Phusion overview

XCode 4 ships a buggy compiler

By Hongli Lai on December 30th, 2011

You may have heard of LLVM, a compiler infrastructure library. You may have heard of GCC, the GNU C and C++ compiler. Those two are completely separate software products, but there exists llvm-gcc which is a GCC frontend that utilizes LLVM. All OS X versions <= Snow Leopard originally shipped with regular gcc, but as of Xcode 4 (which is also the default on OS X Lion) Apple has switched to llvm-gcc as their default compiler. “Hurray”, some people may think, as they heard that LLVM generates better code and/or is faster than default GCC. Unfortunately, the reality is not that great.

llvm-gcc is unmaintained and is considered deprecated by its developers. It has many bugs that will never be fixed. Unfortunately these are not obscure bugs that few people will notice: today, while working on Ruby Enterprise Edition, we encountered several! Calling alloca(0) should return a pointer to the top of the stack. On llvm-gcc however, that code returns NULL but only when compiled with -O2! llvm-gcc also generates subtly different code that causes the MBARI patch set to fail completely.

To make matters worse, consider this program:

#include <alloca.h>

int
main() {
    if (alloca(0) != NULL) {
        return 0;
    } else {
        return 1;
    }
}

Question: when compiled with “llvm-gcc -O2″, does this program exit with 0 or with 1?

That was a trick question. It always returns with 0, even when alloca(0) is broken and actually returns NULL. Turns out the optimizer in -O2 thinks that alloca(0) doesn’t return NULL (even though it does) and replaces if (alloca(0) != NULL) with if (true).

It is unfortunate that we have to declare OS X’s default compiler as being broken. We do not recommend its use. Instead, we recommend people to use /usr/bin/gcc-4.2 (which is the non-LLVM GCC compiler) instead of /usr/bin/gcc (which is a symlink to /usr/bin/llvm-gcc). You can enable this on most build systems by setting these two environment variables:

export CC=gcc-4.2
export CXX=g++-4.2

On a side note, Ruby Enterprise Edition 2011.12 is being released. The official announcement hasn’t been written yet, but we’ve made haste because of the recent Ruby security vulnerability. You can already get the files on the Ruby Enterprise Edition website. An announcement will follow soon.

Update

some people have pointed out that they consider alloca(0) undefined. I guess it depends on the way you interpret the documentation. It would seem pretty clear to me what alloca(0) means, just like what memcpy(x, y, 0) means, but it’s understandable when some people would interpret it as undefined.
Still, alloca(0) failing is only one of the problems that prevented Ruby from compiling properly.

Why alloca(0)?

alloca(0) is used in Ruby Enterprise Edition’s conservative garbage collector, as a way to detect the end of the stack. There’s a bunch of fallback #ifdefs in the code: on platforms that don’t have alloca, it detects the end of the stack by calling a forced-non-inline function which returns the address of its sole local variable, but that assumes that the compiler supports the ‘noinline’ keyword. In any case, all of versions depend on highly platform-specific behavior.

  • Chris Ledet

    Completely agree but the default compiler for Xcode 4.2 (latest) is LLVM 3.0.

  • Joey Gouly

    LLVM GCC has been replaced with DragonEgg. I’m also assuming that future versions of Xcode will ship with Clang by default.

  • Richard Soderberg

    Hi, could you post the Apple Radar bug you’ve filed about this issue to OpenRadar, and link it from the post? Apple responds well to duplicate bug reports, which is much easier when we can all see the bug on OpenRadar to report it properly (especially with the lovely test program above). It’s at openradar.appspot.com if you’re willing.

  • startupgrrl

    From the man page:
    RETURN VALUE
    The alloca() function returns a pointer to the beginning of the allocated space.

    NOTES
    The alloca() function is machine- and compiler-dependent.

    So, one, you’re not allocating any space so it’s not required to return any sane value. Second, it’s not standardized. Things aren’t so much broken as much as you expect them to follow stricter behavior than necessary.

  • http://www.losethos.com Terry A. Davis

    My compiler’s buggy.

    There two types of programmers — those who have written compilers and those who haven’t.

  • Uli Kusterer

    Just curious: Can you give a pointer (standards chapter number, URL or whatever) to documentation that says how alloca() should behave when passed 0 ? All docs I could find simply say it allocates space, returns the pointer. So I wasn’t aware one could get the back of the stack and just assumed that behaviour was undefined or implementation-defined in the standard.

  • http://www.phusion.nl/ Hongli Lai

    None of the docs explicitly mention the behavior of alloca(0), but none explicitly mention that it’s undefined. I think it would be reasonable to think that the behavior is implementation-specific, but consistent, but the example has shown that it’s neither.

  • Joe Y

    >> but none explicitly mention that it’s undefined.

    Everything that is not defined is undefined, you should not rely on undefined feature and say it is a bug, that’s all.

  • ryaner

    Typo in your first paragraph, llvm-gcc is gcc frontend that targets LLVM, not a backend.

  • http://unnali.com Anneli Cuss

    @startupgrrl

    It’s still buggy if the same compiler optimises away alloca(0) != NULL to true! Each compiler knows how they implement their own functions and thus can choose how to optimise them, but here’s clearly a bug because they optimise incorrectly.

  • http://www.phusion.nl/ Hongli Lai

    >> Everything that is not defined is undefined, you should not rely on undefined feature and say it is a bug, that’s all.

    I even disagree on the assertion that its behavior is not defined in the docs. The man pages say that it allocates ‘size’ bytes on the stack. I can easily imagine what allocating 0 bytes on the stack means, thus I do not consider it undefined unless the docs say so.

  • http://www.zorro.com Zorro

    Yeah, I can also imagine what a lot of undefined functions do. That’s why my computer runs on pure imagination!

    The man page on the system you’re using says: “alloca() is machine and compiler dependent; its use is discouraged.” Maybe stick to ruby?

  • http://www.phusion.nl/ Hongli Lai

    @Zorro: I’m glad your computer runs on pure imagination. Your satire and your desire to call me out miss my point though. You are yet again talking about undefined functions, yet I’m disputing the very notion that the docs are not being clear enough about what alloca(0) means.

    As for “machine and compiler dependent”, __asm__(…) is also machine and compiler dependent. That doesn’t mean its behavior shouldn’t be consistent like alloca(0) is in this example.

  • http://www.zorro.com Zorro

    “You are yet again talking about undefined functions, yet I’m disputing the very notion that the docs are not being clear enough about what alloca(0) means.”

    The alloca is specified so that its use is discouraged. This means that the spec says that you should not use the function! Why exactly is this the fault of Xcode (or any other development tool for that matter), is beyond my understanding.

    You say: “Calling alloca(0) should return a pointer to the top of the stack. ” when CLEARLY this is NOT the case. Not even according to the man page for that function! You are just using your own imagination to come up with the behavior of the function, and the blame reality when it does not match your imagination!

    Poor craftsman blames his tools when they are perfectly fine, and when he doesn’t understand how they work.

  • http://www.phusion.nl/ Hongli Lai

    Zorro, seriously, stop the ad-hominem. People are allowed to have different opinions on the subject. I do not belittle or ridicule you for your opinions no matter whether I think they’re right or wrong, and I expect you to treat me with the same respect.

    All this talk about alloca(0) deviates the discussion from the real problem. As I said in the update, alloca(0) is merely used as a way to find out the top of the stack so that the conservative garbage collector can do its work. If you know a better way to find the top of the stack, by all means, let me know.

  • http://www.zorro.com Zorro

    I’m ready to start treating you with respect once you apologize to the good developers of Xcode and clang: they don’t ship a buggy compiler. (At least not in regard to alloca)

    “If you know a better way to find the top of the stack, by all means, let me know.” Hey, maybe I’m not about to do your job if you’re not paying me.

  • http://www.phusion.nl/ Hongli Lai

    I’m criticizing them for shipping an *unmaintained* compiler. That llvm-gcc is unmaintained is a fact. alloca(0) not working properly in my opinion is just one example but whether it is right or wrong does not change the fact that llvm-gcc is unmaintained, or that it emits code besides alloca(0) that makes Ruby crash. Where does Clang even come into the picture? Nowhere did I criticize Clang.

    That said, I criticized them for their decisions. I did not call them inadequate craftsmen or fools or otherwise attacked their person. This is not a name-calling pissing contest where one side “wins” and the other side is supposed to apologize. Having discussions like this is *healthy* for the community. Do as you please, but I will take no part in your games. Your attitude so far tells me enough about you.

    >> Hey, maybe I’m not about to do your job if you’re not paying me.

    I’m not being paid for it either. It’s open source.

  • http://www.zorro.com Zorro

    “I’m criticizing them for shipping an *unmaintained* compiler.” They probably have their reasons for it. For example compatibility, which is pretty important when it comes to compilers. Nothing is stopping you from using clang instead. In fact, it is rather stupid of you to use an unmaintained compiler!

    “I’m not being paid for it either. It’s open source.” Well sucks to be you.

    “alloca(0) not working properly in my opinion” For the nth f***ing time: alloca is working exactly as it is specified to work. There’s nothing wrong with it, and you having an opinion does not change this FACT.

  • Matthijs

    > If you know a better way to find the top of the stack, by all means, let me know.

    Simple workaround: allocate more than 0 bytes on the stack.

  • startupgrrl

    Face it: you don’t want to admit that you’re wrong.

    Whether alloca works properly is not subject to your opinion. The implementation conforms to the spec (or lack thereof) and therefore works properly. Different behaviors under different optimization options should not even be a subject of discussion here. In fact, permitting an optimization pass to affect undefined behavior in an undefined way could be regarded as crucial to compiler performance.

    It is your opinion that although it is working properly, that it should work differently to suit your needs (specifically, to access the stack pointer). Rather than using an explicitly non-portable piece of code to do this (e.g. an asm() for each arch– I’m sure you can figure it out), you prefer undefined behavior which is implicitly non-portable. This is poor engineering for a variety of reasons and is unconvincing. In other words, you hold an opinion, which for the intent of discussing compiler software, is wrong.

  • http://www.phusion.nl/ Hongli Lai

    >> Face it: you don’t want to admit that you’re wrong.

    I am wrong. There, I said it.

    Doesn’t change the fact that Ruby crashes when compiled with llvm-gcc, or the fact that llvm-gcc is unmaintained. Replacing alloca(0) with an asm call is just replacing one evil for another. Either way there’s no portable way to get the top of the stack.

    @Matthijs: yes I tried that. Unfortunately fixing that made Ruby crash on other things.

  • http://www.clove.com Mike

    if you want to waste cycles, you could replace alloca(0) with something like:

    void * my_tos()
    {
    void *ptr = alloca(sizeof(ptr));
    return ptr;
    }

    That should return top of stack and free the allocation.

  • Matt Jones

    I think a lot of commenters are missing the point here – yes, the behavior of alloca(0) is undefined. If it doesn’t return the top of the stack, another method should indeed be used.

    But the *real* point is that the traditional way to handle variations like this (via configure, etc) is BROKEN here – the compiler’s optimization pass makes assumptions which don’t correspond to reality, causing a test program (such as the example above) to return a different result than real application code. *That* is the bug, like silently replacing the expression ‘2+2′ with ‘5’, but only some of the time…

  • Owen

    You’re actually experience a Darwin vs Linux portability issue:

    – While the Linux man page for alloca() specifies that it returns NULL on failure, the Darwin page does not. Since you’re compiling for Darwin, only the latter is relevant. The behavior in the failure case on Darwin is, in the most basic sense, undefined It’s pretty easy to determine that, in practice, an illegal alloca() segfaults on Darwin, but it could equally reasonably make monkeys fly out of your nose.

    – Thus, there are only two possible outcomes of alloca(0): (1) a defined non-NULL pointer or (2) monkeys-out-of-nose. In the former case, the pointer can’t be NULL, and in the latter, the compiler is free to do whatever it wants. Therefore, the compiler can legally remove the NULL check.

    – Whether or not alloca(0) hits case (1) or case (2) is implementation-specific. It looks like you were accustomed to hitting case (1) with GCC, but are now hitting case (2) with LLVM-GCC. It turns out that implementation-specific behavior really does differ between implementations. Welcome to the world of supporting multiple compilers!

  • http://www.mobiliodevelopment.com peter

    That’s why we still stay away from llvm. Probably after year will become stable enough to use it on our projects.

  • http://dojo4.com Ara T. Howard (@drawohara)

    @honglilai – open source is an often thankless task, but not today: thank you for your hard work on ree and passenger.

    we’ve had our own issues with llvm. needing to worry about compiler specific issues doesn’t help the open source community at all. apple should contribute to gcc if it’s too slow: not create compilation chain forks in 40 thousand OSS projects.

  • Chris

    Also, I think /usr/bin/gcc-4.2 is now a symlink to the llvm-gcc in xcode 4.2+ (In 4.1 it was the real GCC).

    People seem to be using this now: https://github.com/kennethreitz/osx-gcc-installer

  • Alexey Borzenkov

    No, /usr/bin/gcc-4.2 is usually a leftover from older XCode installations. Unfortunately, after doing a reinstall of Lion and installing latest XCode gcc-4.2 is not there anymore. Only llvm-gcc and clang are available.

  • RSS

    It seems that apple is releasing more and more buggy development software. Everyone can see how much memory XCode is leaking. Also it seems that Mac OS Lion has new memory management problems. The only thing that apple keeps stable is the iOS operating system (May be because they don’t want jailbreaks or so). I think that the reason for this is that they ACTUALLY DOESN’T HAVE THAT MONEY for development. Strangely they report more money for spending than the US government :P :DDD