MSVC does not unroll simple for-loops: What have I missed?

Viewed 74

In the following code snippet, both functions perform exactly the same functionality and are logically equivalent.

constexpr char a[] = {50, 100};

bool contains_loop(char num) {
    for (int i = 0; i < 2; ++ i) {
        if (a[i] == num) {
            return true;
        }
    }
    return false;
}

bool contains_expanded(char num) {
    return num == a[0] || num == a[1];
}

My test in godbolt shows that, in x64 MSVC 19.30 with /O2 turned on, contains_loop is not optimized to be unrolled. In the assembly code, there is still the counter, which is not in the compiled assembly code of contains_expanded.

        inc     rax
        cmp     rax, 2

In gcc -O3, they are compiled to exactly the same assembly code.

This optimization seems pretty straightforward, but I couldn't get MSVC to optimize it (automatically). What am I missing here?

1 Answers

I am essentially rewriting this answer because I mistakenly called contains_expanded instead of contains_loop which you were asking about.

The short answer is that even with VS2022 (the real thing) and /O2 and /Oi, the loop is, apparently, not unrolled. I even tried favor size instead of favor speed and the result was the same.

I used this rudimentary main

int main()
{
    std::vector<char> vals { 50, 49};
    std::cout << "Test" << std::endl;
    for (auto v : vals)
    {
        const auto val = contains_expanded(v);
        std::cout << "Contains = " << v << std::boolalpha << val;
    }                   
}

My compile optimization switches used /O2 and /Oi. Here is the compile line from the output command tlog file

^C:\USERS\JMOLE\SOURCE\REPOS\TESTCPP\TESTCPP\TESTCPP.CPP
/c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D NDEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /permissive- /Fo"X64\RELEASE\\" /Fd"X64\RELEASE\VC143.PDB" /external:W3 /Gd /TP /FC C:\USERS\JMOLE\SOURCE\REPOS\TESTCPP\TESTCPP\TESTCPP.CPP

Here is the listing for the call to contains_loop in my version.

    const auto val = contains_loop(v);
00007FF7234D1224  xor         eax,eax  
00007FF7234D1226  cmp         byte ptr [rax+r15],bl  
00007FF7234D122A  je          main+0AAh (07FF7234D123Ah)  
00007FF7234D122C  inc         rax  
00007FF7234D122F  cmp         rax,2  
00007FF7234D1233  jl          main+96h (07FF7234D1226h)  
00007FF7234D1235  xor         sil,sil  
00007FF7234D1238  jmp         main+0ADh (07FF7234D123Dh)  
00007FF7234D123A  mov         sil,1  

So iooks like the loop is not unrolled in MSVC pretty much regardless of what optimizations you choose. If you are missing something I don't see what it is

Related