Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time loop optimisation

I am finding it a bit hard to understand why the following results in a compile time calculation. I have read this, this, this and a lot more questions on stackoverflow that tell me the following code( at least to my understanding) should not be computed at compile time because of the while loop (The code is just an example to illustrate the question):

template< unsigned N >
constexpr unsigned isStringNice(const char (&arr)[N], unsigned pos = 0)
{
    //we do not like the 'D' char :)
    int currPos = 0;
    while(currPos < N){
        if(arr [currPos] == 'D'){
            throw 1;
        }
        currPos ++;
    }
    return 1;
}
constexpr unsigned isIdxValid( unsigned idx, unsigned len){
    return idx >= len?  throw 1 : idx;
}

template< unsigned N >
constexpr char nth_char(const char (&arr)[N], unsigned pos){
  return  isStringNice(arr),isIdxValid(pos, N),arr[pos];
}

int main(){

  constexpr char b = nth_char("ABC", 2);

  return b;
}

this outputs with no flag the following assembly code (gcc 8.2 , thank you Godbolt) main:

push    rbp
mov     rbp, rsp
mov     BYTE PTR [rbp-1], 67
mov     eax, 67
pop     rbp
ret

and with -O3

main:
        mov     eax, 67
        ret

Notice as there is no jump in there, no branch on the while condition, nothing. I had the impression that for-loops and while loops were not possible to be evaluated at compile time. However, the compiler (gcc 8.2) evaluates the result at compile time. My only thought is this happens because of loop unrolling, so i tried using -fno-unroll-loops, but this results in the same assembly code.On the other hand from my experience, this flag is more like a compiler suggestion than a guarantee and gcc might still unroll the loop even though the flag is set.

Short version of my question: How come my while loop in a constexpr function is evaluated at compile time?

like image 947
eucristian Avatar asked Jan 02 '23 03:01

eucristian


1 Answers

In C++14, constexpr function requirements were relaxed.

Previously, in C++11, constexpr functions could only contain typedefs, static_asserts and usings, but only a single return statement.

In C++14, it became possible to use loops in constexpr function bodies.

Because b was declared as constexpr char, it must be evaluated at compile time. The compiler then optimised out the isStringNice function, as it is not used at run time.

like image 50
Artyer Avatar answered Jan 09 '23 05:01

Artyer