Compilers Can Be Sexy Too
July 15th, 2007
Scott recently pointed me to the LLVM project and I have to say I’m impressed. More than impressed. I’m downright drooling on my keyboard. The site even has a page where you can throw it some C/C++ code and it’ll spit out the corresponding assembly code. I tried it out and I have only one question: is this love?
The thing that really did it for me is when I saw it compile this:
typedef struct some_type_t
{
int a;
int *b;
} some_type_t;
typedef struct int_t
{
const int *a;
const some_type_t *b;
} in_t;
typedef struct out_t
{
int *a;
int *b;
int *c;
} out_t;
static const int iters = 10;
static const int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
#define lengthof( a ) (sizeof( a ) / sizeof( a[0] ))
void fn1( const in_t *in, const out_t *out )
{
if( out->a )
*out->a = *in->a;
if( out->b )
{
int i;
*out->b = *in->a + (*in->b).a;
for( i = 0; i < iters; i++ )
*out->b += arr[i % lengthof( arr )];
}
if( out->c )
*out->c = *in->a - *(*in->b).b;
}
int main( int argc, char **argv )
{
int ina, intb;
int outa, outb;
some_type_t inb;
in_t in;
out_t out;
(void)sizeof( argv ); //get rid of the warning
ina = 42;
intb = argc;
inb.a = 13;
inb.b = &intb;
in.a = &ina;
in.b = &inb;
out.a = &outa;
out.b = &outb;
out.c = 0;
fn1( &in, &out );
return outb;
}
Into this:
define i32 @main( i32 %argc, i8** %argv )
{
entry:
ret i32 100
}
Which is the same as this:
int main( int argc, int **argv )
{
return 100;
}
Of course, MSVC can do the same thing, and I assume GCC with -O3 will as well - but neither the closed-source MSVC nor the GPL’d and (as I understand it) obscenely complex GCC can be readily integrated into other (larger) applications.
One interesting thing: if you increase iters to 100, neither LLVM nor MSVC will unroll the loop (and thus won’t reduce the
whole program to a single ret instruction. But LLVM still manages to inline the function and remove the dead branches (both
the if( out->c ) block and the out->a block who’s result is never read) which then allows it to remove most of the input-argument
setup code as well. MSVC, however, doesn’t inline fn1 (without being told to with a __forceinline) and thus can’t tell that some
code paths will never be used.
This is probably just a side-effect of the order in which the two run their optimizations (MSVC likely does partial loop-unrolling before inlining, which means that by the time the inline phase runs the function’s already been made too big to qualify) so there are probably cases where MSVC will make better decisions, but I thought this was interesting none-the-less.
Now, to figure out this whole Linux thing and get a project off the ground.
/wipes drool off of keyboard
Sorry, comments are closed for this article.