JonHoyle.com Mirror of MacCompanion
http://www.maccompanion.com/macc/archives/September2009/Columns/AccordingtoHoyle.htm

macCompanion
macCompanion magazine

macCompanion

Blog

Archives

Products

Services

About Us

FAQs

Resources

 

Consultants

Developers

Devotees

Downloads

"Foreign" Macs

Forums

Hearsay

Link Lists

Mac 3D

Macazines

Mac Jobs

MUG Shots

News

Radio

Reviews

Think Different

Training

 

Download this issue


According to Hoyle

According to Hoyle...

Mac OS X 10.6 Snow Leopard: Xcode 3.2 Compilers

 

macCompanion

September 2009

by Jonathan Hoyle

jonhoyle@mac.com

http://www.jonhoyle.com

 

 

In an industry that is perpetually bogged down in delays and late deliveries, Apple beat its own expectations and released Mac OS X 10.6 Snow Leopard at the end of August.  Being a developer column, we will dive into the new developer tools.  In particular, we are going to examine this month the new compiler options available with Xcode 3.2: gcc 4.2, LLVM and Clang.

 

 

gcc 4.2


We Mac developers tend to think in terms of versions of Xcode (the complete development environment provided by Apple), rather than versions of gcc (the open source compiler used by Xcode).  Xcode 1.x was used to develop for Mac OS X 10.3 Panther, and it used the gcc 3.3 compiler, a very popular compiler used amongst Unix programmers.  Despite its ubiquitous use, it lacked the full ANSI/ISO C++ compliance that was available in the gcc 4 line.  So it was only natural that when Xcode 2.0 was released (as part of Mac OS X 10.4 Tiger), the default compiler was moved up to gcc 4.0.  Although Xcode continued to evolve, with versions 2.1, 2.2, 2.3, 2.4, 2.5, 3.0 and 3.1 spanning over four years (incorporating universal binaries, Leopard support, 64-bit capabilities, etc.), the default version of gcc they used never exceeded version 4.0.1.

Four years is a long time in this industry.  It is high time for a change.  With Xcode 3.2, the default compiler has been updated to gcc 4.2 (although 4.0 is still available for users to select if needs be). gcc 4.2 was available as an option in Xcode 3.0/3.1, but it becomes the default in 3.2.  There are a number of nice features to 4.2, in addition to tweaking ANSI/ISO adherence to be in even better compliance. Some of these features involve additional TR1 library support for the upcoming C++0x standard.  These include the complex<> and random<> classes, C compatibility header files and even a lock-free version of the shared_ptr<> class.

But the biggest benefit that gcc 4.2 brings to the table is OpenMP, a multi-processor API which will take full advantage of those multi-core Macs that are selling so well now.  And unlike Grand Central Dispatch, OpenMP is not specific to Snow Leopard or even Cocoa, so can be used on code you build for other platforms.  An example is best to explain it further:

Consider a simple, yet common, task of summing the values of two arrays and placing their sum in a third array:

    
for (i = 0; i < numItems; i++)
       z[i] = x[i] + y[i];

Suppose this snippet lives in a critical area of code which you would like to optimize. Machines with multiple processors could be used to parallelize this loop, by dividing the work across each processor core, dramatically improving performance. Unfortunately, writing the threading code and implementing the required mutexes to support this is quite complex, and would necessarily require a great deal of code debugging to get it right. If such a procedure is needed throughout different areas of the code, it gets even worse.

With OpenMP, however, only two directives need be added:

    // parallelize this loop
    #pragma omp parallel shared(x,y,z,chunk) private(i)
    {
        #pragma omp for schedule(dynamic,chunk) nowait
        for (i = 0; i < numItems; i++)
           z[i] = x[i] + y[i];
    }

The first #pragma informs the compiler which data objects are being shared across threads, and which is private. The second #pragma handles the for loop chunking. This code is an order of magnitude easier to write, as it simply directs the compiler to perform the parallelization. OpenMP is implemented by POSIX threads, thus making them quite solid and secure.

As mentioned above, OpenMP is a platform-independent standard and thus can be used in cross-platform code.  So in addition to Mac OS X, you can compile this same code snippet in Visual C++ 2005 Professional and Sun Studio.

 

 

 

LLVM


One of Apple's newest compiler strategies is LLVM, which stands for Low Level Virtual Machine.  LLVM is an open source compiler infrastructure designed for compile-time and run-time optimizations.  Although LLVM targets itself nicely as a replacement for gcc, its internal philosophy is a bit different.  In LLVM, compilers are built as a set of reusable libraries and supports applications with shared components.  Furthermore, it is designed for performance, with much faster compile times and more optimized code generation than found in gcc.  (Ask any former Metrowerks CodeWarrior user who had to move to Xcode, what they thought of gcc's performance.)

A compiler can be thought of as containing three parts: the front end (the language parser), optimizer, and the back end (code generator).  The front end (obviously) differs from language to language, the optimizer may or may not, and the back end (if the compiler is written well) should be completely language independent.  LLVM provides only these last two pieces, and relies on other (compatible) front ends.  gcc 4.2's front end is written to be compatible with LLVM (which is why you'll sometimes see Apple documentation refer to its LLVM use as "LLVM-GCC 4.2").

It should be noted that although Apple is concerned only with three language front ends in Xcode (C, C++ and Objective-C), the open source LLVM with work with many other gcc front ends, including Fortran, Ada and D.

LLVM has nearly all of the major gcc 4.2 features, including blocks, stack canaries, OpenMP and the like.  LLVM's code generation is significantly better for 32-bit compilation (particularly for Intel).  Its 64-bit Intel generated code is about the same quality as gcc's, maybe marginally better.  LLVM does not support 64-bit PowerPC compilation.

 

 

 

Clang


Like LLVM, Clang is a non-traditional compiler technology.  Clang however is only a language front-end, designed to be used with LLVM's optimizer and back-end.  With Clang, all remaining vestiges of gcc can finally be removed.  However, only Clang's C front end is fully complete.  The Clang Objective-C compiler is usable, but not quite finished.  Sadly its C++ (and by extension its Objective-C++) front end compilers will not be completed before 2011, so is not supported in Xcode 3.2.  However, if you can use Clang, you will see a nearly threefold performance improvement, in compile times, a major improvement over gcc.  (I can hear old CodeWarrior users now sighing "Finally!").

With a new front end, there are always concerns about language syntax changes in existing code.  Many moving from Xcode 1.5 to Xcode 2.x (gcc 3.3 to gcc 4.0) a few years ago will remember having to make numerous code changes.  These changes were, for the most part, good because gcc became tighter in its ISO compliance, and thus changes were made to essentially fix "badly written" code.  However, moving from gcc 4.x to Clang should not involve nearly as many code changes, although some may be required.

One change is simply in defaults.  In gcc 4.x, the default C compiler is the original (ancient) C89 parser.  Users had to manually change to C99 to get more modern features.  Clang correctly uses C99 as the default.  Therefore, if you have some old code which is not C99 compatible (and you don't have time to fix it), you will have to change your Clang settings to use C89.

In Clang's Objective-C front end, a number of incorrect and deprecated constructs are not supported.  These include various "bad" casting calls and sizeof() being used on NSArray.  Here are some Objective-C examples which compile in gcc 4.x but fail in Clang:

   [(myInterface *) super add:4] // should just be [super add:4]
  (int *) addr = val; // can't cast l-value, do: addr=(float*) val
  sizeof(NSArray// use: class_getInstanceSize([NSArray class])

Clang is also command-line compatible with gcc, so build script can be simply changed.  For example:

   /Developer/usr/bin/clang hello.c -o hello

Perhaps the best thing Clang brings to the table is better error and warning messages.  This is yet another complaint that former CodeWarrior users have (rightly) squawked about over the years.  Obscure and arcane gcc messages, which are often more deceiving than helpful, have been replaced in Clang with more appropriate and helpful ones.  An example Apple itself has used to demonstrate this improvement is a simple one in which a missing header causes NSString not to be defined.  In which case the following line of code will fail in compilation:

    NSString  *s = @"I like Clang";

In gcc, that compiler error looks like this:

  Expected '=', ',', ';', 'asm' or '__attribute__' before '*' token


My God, we put a man on the moon 40 years ago, but we can't get a better error message than that?  A definite WTF.  With Clang however, the error message reads:

 
 
Unknown type name 'NSString'

A simple, but definite, improvement.

 

 

Conclusion


Things are really looking up in the Mac development world.  Simply put, gcc 4.2 is better than gcc 4.0.1, LLVM-GCC 4.2 is better than gcc 4.2, and LLVM-Clang is better than LLVM-GCC 4.2.  It's all good.  Well, it may be all good, but sadly it's not all finished.  Clang still does not have C++ enabled, making the most promising of the compiler technologies the least useful.  However, unless you have some very fragile code, or are still compiling 64-bit PowerPC (and why would you be needing to do that?), you should be able to move to LLVM today.

 

 

 

To view other According to Hoyle articles, visit:

http://www.jonhoyle.com/maccompanion 

 

http://www.maccompanion.com/macc/archives/September2009/Columns/AccordingtoHoyle.htm