Can I tell my compiler to ignore the side effects of a statement or a function?

742 views Asked by At

Suppose my code has the following function:

inline int foo() {
    bar();
    int y = baz();
    return y;
}

and suppose that bar() and baz() have side-effects.

If I write:

int z = foo();
printf("z is %d\n", z);

then obviously both bar() and baz() have to be executed. But if I write:

foo();

and not use the return value, the only reason to call baz() is for its side effects.

Is there a way I can tell my compiler (for any of: GCC, clang, MSVC, Intel) something like "you may ignore the side effects of baz() for purposes of deciding whether to optimize it away"? Or "you may ignore the side effects of this instruction for purposes of deciding whether to optimize it away"?

This question is phrased for C and C++ but could be relevant for all sorts of procedural languages. Let's make it C++ because that's what I use the most right now.

Note:

  • bar(), baz() are not under my control.
  • The signature of foo() may not be altered. Of course I could use some proxy and apply baz() lazily, if I so wanted; or just write a different function for the case of not using y. That's not what I'm asking.
2

There are 2 answers

4
Nate Eldredge On BEST ANSWER

As an extension to standard C and C++, gcc and clang support the pure function attribute which informs the compiler that the function has no side effects and unneeded calls can be deleted. If you declare a function with this attribute, the compiler will believe you, even if you are lying.

#include <iostream>

static int foo() __attribute__((pure));

static int foo() {
  std::cout << "I am a side effect!" << std::endl;
  return 17;
}

int main() {
  foo();
  return 0;
}

This prints no output. Note that clang will give a warning that you are ignoring the return value of a pure function.

(Peculiarly, when you do something similar in C, gcc optimizes out the call with -O0, but leaves it in with -O2!)

The definition of the function does not need to be in scope for this to work, and you can re-declare a function with this attribute even if a previous declaration is already in scope. So it can also be used for library functions. Example.

Of course, lying to the compiler is always at your own risk, and conceivably this could have potential ill effects that I haven't thought of. It may be a useful hack, if you are willing to carefully scrutinize the generated code to make sure it is really doing what you want, but I wouldn't trust it too far.

1
supercat On

There are a fair number of situations in which side effects of a function will only be relevant if the return value ends up being used, but as far as I can tell, neither C nor C++ has any means of indicating them.

As a common example, it may be useful to have routines which perform some computations and return the results, but will raise a trap if (e.g. because of overflow) they are unable to return a result that was not observably wrong. Depending upon how a function is being used, having it trap may be infinitely preferable to having return an observably-wrong answer, but might not be preferable to having it behave in a manner indistinguishable from having somehow yielded the correct answer. If a program performs a dozen computations, but only uses ten of them, skipping the unused computations entirely would likely be more efficient than generating code to see if overflow would occur within them.

Perhaps what would be helpful would be a construct to test whether a compiler can guarantee that the value of a particular lvalue will never observably affect program behavior; an implementation would always be allowed to say that it can't, but in situations where a compiler could offer such a guarantee, it would allow suitably-written code to say, e.g. "If the value of x, which this function is going to return, will never end up being used, don't bother to compute it; otherwise, check for overflow and trap if it would occur; if the return value is used and there would be no overflow, compute the value of the function."

I'm unaware of any C implementations that offer such a feature, however.