Vala and the comma operator

One of the more interesting parts of working on a Vala project is occasionally having to read through the C source the Vala compiler generates.  Sometimes it’s to investigate a bug (maybe your own, maybe the compiler’s) and sometimes it’s just out of curiosity.

Personally, I think the C code Vala produces is pretty darn good as far as machine-generated code goes.  (And I’ve seen some horrible machine-generated code, not including obfuscators and their ilk.  Which reminds me, have I ever told you about when I met Phil Katz?  Another time.)  Certainly I feel I could produce much prettier code by hand, but that’s the point: I would have to do it by hand.  A lazy programmer is a good programmer.

Take a look at this typical example of Vala’s generated C code:

gboolean dimensions_has_area (Dimensions *self) {
    gboolean result;
     gboolean _tmp0_ = FALSE;
     if ((*self).width > 0) {
         _tmp0_ = (*self).height > 0;
     } else {
         _tmp0_ = FALSE;
     }
     result = _tmp0_;
     return result;
}

If an interview candidate at Yorba put this up on the whiteboard, I imagine the first thing we’d ask would be, “Ah … anything you think you could do to clean up this function?”

This isn’t to knock Vala or the compiler or the wonderful work Jürg and Team Vala have done.  And yes, I know, gcc will optimize this down to machine code that would be pretty much like the machine code it would produce if I hand-coded this.  And, of course, unless this is in hot loop (which it’s not, last time I checked), none of this matters anyway.

In short, I lose zero sleep over this.

But consider this line, which is also not atypical:

scaled = (dimensions_floor ((_tmp2_ = (dimensions_init (&_tmp0_, (gint) round (scaled_width), (gint) round (scaled_height)), _tmp0_), &_tmp2_), (_tmp3_ = (dimensions_init (&_tmp1_, 1, 1), _tmp1_), &_tmp3_), &_tmp4_), _tmp4_);

Ah … okay, hold on.  Don’t run off.  It’s not as bad as you think.  It’s just that this makes much more sense to the C compiler than it does to you.

My advice to anyone who dives into Vala’s generated code is to study up on the comma operator.  I suspect most programmers (maybe 98% of them!) have never used the comma operator outside a for loop, and even then, rarely.  Most of them would say that the comma operator is only used to perform multiple statements (actually, “operands” in the parlance) on a single line of code.  That is, it’s a handy way when writing a for loop to squeeze in multiple statements inside each of its three parameters.

Which is true, save for one thing: With the comma operator, the statement as a whole evaluates to the final operand.  Thus, the initializers in this for loop evaluate to 42:

for (i = 0, j = 42; i < j; i++)

The initializer isn’t being assigned to anything, so we don’t notice, so we never learn.

Looking back over that C code up there, mentally weed out the _tmp0_, _tmp1_, etc. and see it for what it is: Initializing two dimensions structs (one goes into _tmp2_, the other into _tmp3_), which are passed to dimensions_floor as parameters, which puts its result into _tmp4_ (whose address is passed to dimensions_floor as its third parameter).  The final operand of the comma operator is, in fact, _tmp4_, which then is returned to scaled.  The comma operator makes all this happen.

Here’s that line in Vala, by the way:

Dimensions scaled = Dimensions((int) Math.round(scaled_width), (int) Math.round(scaled_height)).floor();

Leave a Reply

Your email address will not be published. Required fields are marked *