Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C static inline parameter evaluation optimization

Assume an API in which every function returns an error code which is zero in case of no error and nonzero for error values.

Let

int foo(...);
int bar(...);

be functions in this API. Let there be a code fragment in which foo and bar have to be called in order and foo and bar should always be called, regardless of previous error but the first returned nonzero error code is to be propagated, i.e.

int foobar(...)
{
    int rc = 0, rc_;
    /* ... */
    rc_ = foo(...); rc = rc ? rc : rc_;
    rc_ = bar(...); rc = rc ? rc : rc_;
    return rc;
}

Writing the rc,rc_ multiplexing is tiring and error prone (no matter if a ternary operator, if/else or something else is used).

Let there be an error propagating helper function

static inline
int rc_propagate(int r, int p){ return p ? p : r; }

This could be used in foobar like this

int foobar(...)
{
    int rc = 0;
    /* ... */
    rc = rc_propagate(foo(...), rc);
    rc = rc_propagate(bar(...), rc);
    return rc;
}

Does the C standard allow to optimize by pulling the evaluation of the first parameter of rc_propagate, a static inline function, into the ternary so that it may not get executed due to the ternary operator evaluation rules if the second parameter p were nonzero?

like image 843
datenwolf Avatar asked Jul 03 '16 23:07

datenwolf


1 Answers

A compiler(or hardware for that matter) is allowed to optimize the program1 as long as the program stays the same, i.e. you cannot prove that the program that was executed was different from the one you wrote.

In this case the program you wrote will always call the external function because it isn't present in the ternary operator where it may not be evaluated. The function may have different interesting side-effects and is unknown to the compiler. This means that the optimized version of the program will have to call the external function at some point (code may be reordered) to preserve that behavior.

If that weren't true you could prove that the program that was executed wasn't the same as the one you wrote. Doing this would be easy; put a printf(or equivalent) statement into the external function call.

1. A concept of an abstract program which exists when it is executing, not the generated machine code or executable.


Using an argument from authority, you can see that no compiler will actually optimize the calls to foo() and bar() out:

gcc versions 4.9.2, 5.3,or 6.1 with -O2:

foobar():
pushq   %rbx    
call    foo()    
movl    %eax, %ebx    
call    bar()    
testl   %ebx, %ebx    
cmove   %eax, %ebx    
movl    %ebx, %eax    
popq    %rbx    
ret

clang versions 3.7.1, or 3.8 with -O2:

foobar():                          
pushq   %rbx    
callq   foo()    
movl    %eax, %ebx    
callq   bar()    
testl   %ebx, %ebx    
cmovnel %ebx, %eax    
popq    %rbx    
like image 57
2501 Avatar answered Sep 20 '22 23:09

2501