cinlinecompiler-optimization

Is the C compiler allowed to do out-of-order optimization around an inlined function?


Supposing I have a function foo() and an inline function bar():


inline void bar(void) {
    // do some stuff
}

void foo(void) {
    // some code
    bar();
}

Is the C compiler allowed to rearrange / interleave the sequence of "do some stuff" and "some code" instructions? (Something which would not be allowed if bar was not inlined.)

To make it clear, could the compiler arrange the code to be some instructions of "some code" after instructions of "do some stuff"?

I have a situation where this rearrangement is happening, and I want to know if that is allowed compiler behavior.

Edit: corrected "after instructions"

I'm currently using a C11 compiler.


Solution

  • It depends on what "do some stuff" means. The C standard defines a side effect as (5.1.2.3):

    Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

    Note that it says modifying an object which means a write access to any variable, regardless of if that variable is volatile or not. Furthermore, the term "sequence point" is defined as:

    The presence of a sequence point between the evaluation of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B.

    In case of functions there is at least one sequence point after argument evaluation before the function is called, and one at the end before return. So strictly speaking the above would mean that no re-ordering is allowed what-so-ever.

    However, optimizations are allowed to remove side effects that are not "needed":

    In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

    So for example something like void f (int y) { int x=y; } contains the side effect of writing to the object x but that value isn't used anywhere, so this whole function could get optimized away without changing the behavior of the program. However if this function definition isn't available to the compiler when it generates the function call code, then it wouldn't be able to assume anything about that function and then it would also be unable to optimize/inline it.

    Specifically, optimizations must follow something called "observable behavior", which is defined as follows:

    The least requirements on a conforming implementation are:

    • Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
    • At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.
    • The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

    This is the observable behavior of the program.


    In plain English:

    The compiler is free to re-order the code across a function (inlined or not) or optimize it away entirely, if 1) it can guarantee that the result is the same as it would be without re-ordering/optimizing and 2) there are no volatile variables involved.