Translating C to C — An example

The C statement

Let’s look at some C code to set an integer m to the larger of the integers x, y, and z.

if (x >= y && x >= z) {
    m = x ;
} else if (y >= z) {
    m = y ;
} else {
    m = z ;
}

If you use the rules of the control structures section, which ignores expression evaluation, you’ll get something like the following:

      int τ1 = x >= y && x >= z ;
      if (τ1 == 0) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

Now we have to worry about evaluating the expression with the &&. Here we use the rules for dealing with logical expression. This is going to get very messy. First, we get the following:

      int τ1 ;
      if (! (x >= y)) {
          τ1 = 0 ;
      } else if (! (x >= z)) {
          τ1 = 0 ;
      } else {
          τ1 = 1 ;
      }
      if (τ1 == 0) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

Then we go back and apply the rules of the ifelse, and it gets even worse.

      int τ1 ;
      int τ3 = ! (x >= y) ;
      if (τ3 == 0) goto λ4 ;
      τ1 = 0 ;
      goto λ6 ;
λ4:   int τ4 = ! (x >= z) ;
      if (τ4 == 0) goto λ5 ;      
      τ1 = 0 ;
      goto λ6 ;
λ5:   τ1 = 1 ;
λ6:   if (τ1 == 0) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

Huh?

Yep. That’s pretty silly. Let’s do some optimiztions. First simplify those comparisions.

      int τ1 ;
      int τ3 = (x < y) ;
      if (τ3 == 0) goto λ4 ;
      τ1 = 0 ;
      goto λ6 ;
λ4:   int τ4 = (x < z) ;
      if (τ4 == 0) goto λ5 ;      
      τ1 = 0 ;
      goto λ6 ;
λ5:   τ1 = 1 ;
λ6:   if (τ1 == 0) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

There are some branching inefficiences here. For example, in one place τ1 is set to 0 before a transfer to λ6 where τ1 is immediately compared to 0 before transfering to λ1. Let’s speed that up a little. We can even eliminate τ1.

      int τ3 = (x < y) ;
      if (τ3 == 0) goto λ4 ;
      goto λ1 ;
λ4:   int τ4 = (x < z) ;
      if (τ4 == 0) goto λ5 ;      
      goto λ1 ;
λ5:   m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

Next we can remove a few of those unconditional goto’s by optimizing the tests.

      int τ3 = (x < y) ;
      if (τ3 != 0) goto λ1 ;
      int τ4 = (x < z) ;
      if (τ4 != 0) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   int τ2 = y >= z ;
      if (τ2 == 0) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

You might find this a little easier to read if we just got rid of τ2, τ3, and τ4.

      if (x < y) goto λ1 ;
      if (x < z) goto λ1 ;
      m = x ;
      goto λ3 ;
λ1:   if (y < z) goto λ2 ;
      m = y ;
      goto λ3 ;
λ2:   m = z ;
λ3:

That’s what any self respecting C compiler would do.