Search code examples
c++exceptionoptimizationtry-catchcompiler-optimization

Is the compiler able to optimize try/catch into a simple goto?


It seems to me that if you have some C++ code like this:

int f()
{
  try {
    if( do_it() != success ) {
      throw do_it_failure();
    }
  } catch( const std::exception &e ) {
    show_error( e.what() );
  }
}

The C++ compiler should be able to optimize the throw and catch into almost a simple goto.

However, it seems to me from my experience viewing disassembly and stepping through code that the compilers always jump through the very messy exception handling libraries.

Why do they do that? Is there some language requirement that prevents optimizing? What if it was:

int f()
{
  try { throw std::runtime_error("Boo!"); }
  catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}

Why does the compiler not just rewrite that as

int f()
{
  std::cout << "Boo!" << std::endl;
}

Solution

  • I think the accepted answer is quite uninformative if not wrong, so even after so many years I feel the need to offer a proper answer.

    Speculating on why the compiler implementors chose to not put effort on any particular feature is just, well... speculation. The fact that exceptions are thrown only in exceptional circumstances is not generally felt as a reason to not optimize the performance of such code. On the contrary, even though it is true that throwing code is not optimized at the expense of non-throwing code, the exception throwing and handling infrastructure is nevertheless optimized very carefully.

    Furthermore, that piece of code might feel so contrived that is not worth considering, but this is not true: it may result from inlining and optimizing of much more complex code, and optimizing it could result into simpler code that allows other optimization passes to fire, or the containing function to be further inlined. Optimization passes like these, when correct and efficient to implement, are always worth of at least being considered, no matter how contrived the original piece of code might look. Otherwise, even fundamental passes like dead code elimination would be avoided because "dead code should not be written in the first place". Which is obviously not the case.

    Hence I just do not agree with the accepted answer. The fact that exceptions should be thrown exceptionally is not the reason why this code is not optimized.

    The reason is purely technical, and is explained in this email from the clang development mailing list: http://lists.llvm.org/pipermail/cfe-dev/2015-March/042035.html

    To summarize, the language allows code called inside the catch block to rethrow the exception in any point "without ever having seen" the exception object:

    void g() { throw; }
    

    Hence, consider the OP code:

    int f()
    {
      try { throw std::runtime_error("Boo!"); }
      catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
    }
    

    For what the compiler is concerned, e.what(), or the two invocations of operator<<, may rethrow the exception, hence optimizing away the exception handling code would break the semantics of the program.

    Ensuring this is not the case would require "whole-program knowledge", as written in the above email message. Even simpler cases could be optimized, such as:

    int func() {
      try {
        throw 42;
      }catch(int x) {
        return x;
      }
    }
    

    The above code can be transformed into return 42. There are no technical reasons that impede it.

    Still, most common compilers do not do it (godbolt). This time, we can tell from an actual source, the email linked above, that Clang developers (we cannot say anything for other compilers) do not think this optimization to be worth, probably because it would only apply to catch blocks that do not do function calls.

    Anyway, the message says nothing about whether they would accept a patch to do so.