Search code examples
c++gccoutputc++17compiler-explorer

Comparing the outputs between a function and a function template on Compiler Explorer


I implemented simple versions of the factorial function which both can be seen here. I'm using GCC Trunk. There are 2 source files, the first being the function version and the second being the templated version. Each source file has 2 attached compilers with an associated output. The only difference between the two attached compilers for each source file is that the lefthand compiler has no compiler flags or optimizations turned on, and the righthand version has -O3 set to turn on level 3 optimizations.

Here are my functions:

// Function Version
int factorial(int n) {
    if (n == 0 || n == 1) return 1;
    if (n == 2) return 2;
    return (n * factorial(n-1));
}

// Template Version:
template<unsigned N>
static auto constexpr factorial_t() {
    return (N * factorial<N-1>());
}

template<>
auto constexpr factorial_t<0>() {
    return 1;
}
template<>
auto constexpr factorial_t<1>() {
    return 1;
}
template<>
auto constexpr factorial_t<2>() {
    return 2;
}

Now, when I run these in my installed IDE Visual Studio 2017 on my local PC using c++17, I am getting the expected output returned back from main(), and the returned value is correct for both implementations.

I ported this to Compiler Explorer to test other compilers and their optimizations to compare their generated assembly instructions. This is a fairly straightforward process.

Now when I run these functions as such:

source#1

int main() {
    return factorial(6);
}

source#2

int main() {
    return factorial_t<6>();
}

Compiler Explorer generates the following instruction counts...

  •          |   Assembly Instruction Count   |
    
  • Type     | Without O3 | With O3 Turned On |  
    
  • Function |     34     |       29          |
    
  • Template |     50     |        3          |
    

All is good.

All four program executions return a value of 208.

Now to my question:

However, it's not obvious within the assembly of the first and third compilers directly without doing some register math, but in the second and last with -O3 turned on for both the function and template versions, the value 720 is being stored into the EAX register before main()'s return call. Why is Compiler Explorer displaying: Program returned: 208 instead of 720?


Solution

  • main is defined to return an int. After main returns the c++ runtime will call std::exit (or equivalent code) with the value returned from main.

    std::exit also takes an int exit code. How this exit code is turned into a process return code is implementation defined.

    On Unix the return code for a process is usually a single unsigned byte so the int is simply truncated to fit into 0 - 255.

    On Windows process return codes are 32-bit signed integers so the int value passed to std::exit is returned directly.