Do likelihood attributes make sense with a single if statement?

Cppreference and this documentation do not state explicitly that likelihood attributes won’t work with a single if statement. Or, I just do not understand what is meant by alternative path of execution. So that’s my question, will the attribute, say, [[unlikely]], work in the case below?

if (condition) [[unlikely]] {
    do_stuff();
}

>Solution :

Yes, it makes sense. The alternative, [[likely]], path is the one where the condition is false, that is, the path not calling do_stuff();. That becomes the path it’ll try to optimize for.

Example:

#include <iostream>

inline void do_stuff() {
    std::cout << "Surprise!\n";
}

int main(int argc, char**) {
    if (argc == 0) [[likely]] {
        do_stuff();
    }
}

Assembler with [[likely]]:

.LC0:
        .string "Surprise!\n"
main:
        test    edi, edi
        jne     .L4
        sub     rsp, 8
        mov     edx, 10
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        xor     eax, eax
        add     rsp, 8
        ret
.L4:
        xor     eax, eax
        ret
_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit

With [[unlikely]] (and without attribute at all):

.LC0:
        .string "Surprise!\n"
main:
        test    edi, edi
        je      .L8
        xor     eax, eax
        ret
.L8:
        push    rax
        mov     edx, 10
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        xor     eax, eax
        pop     rdx
        ret
_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit

Those are two slightly different outcomes and without knowing too much about assembler, I’d say the effect of putting [[likely]] there is clear. It looks to me that putting [[likely]] there made it inline the function while [[unlikely]] left it as a function call.

Leave a Reply