Paul <
peps...@gmail.com> writes:
> I posted the below code on another thread.
> A respondent said that the code should be "optimized away" by a good compiler.
> Presumably, this means that because basicPopcount has no side effects,
> and because main() doesn't do anything with the local variables it
> generates by calling basicPopcount, many of the basicPopcount calls should
> simply be skipped because the compiler has worked out that the computation
> would be pointless.
Yup. That's what happens with, for example, with g++ 8.2.0 with -O1 and
above.
> I attempted to test this theory with Godbolt's compiler explorer.
> I have no formal education in assembly or computing more generally,
> so the feedback is cryptic.
> Complete code and compiler explorer data are copy-pasted below.
> Compiler is x86-64 gcc 8.2
> What does the compiler feedback say about whether the computations
> are skipped in the way described above?
No. What you show has a main function that loops calling your
basicPopcount function:
> main:
> push rbp
> mov rbp, rsp
> sub rsp, 16
> mov DWORD PTR [rbp-4], 0
> .L8:
> cmp DWORD PTR [rbp-4], 9999999
> jg .L7
> mov eax, DWORD PTR [rbp-4]
> mov edi, eax
> call basicPopcount(int)
> add DWORD PTR [rbp-4], 1
> jmp .L8
> .L7:
> mov eax, 0
> leave
> ret
Maybe you did not request optimisation?
A couple of points about the function itself...
> int basicPopcount(int x)
> {
> int count = 0;
> int place = 0;
> // An overflow bug can arise in this method for large values of x
> // So make sure types are large enough
> while(1ull << place <= x)
> if(1ull << place++ & x)
> ++count;
>
> return count;
> }
The term "overflow" is a little odd here. There are two things to worry
about. The first is shifting a 1 bit into the sign position and you've
dealt with that but using 1u. The second thing is not really "overflow"
per se. Shifting by the width of the (promoted) left-hand operand is
undefined so the only way to give your loop defined behaviour is use a
type that is wider than int. Unfortunately, unsigned long long int is
not guaranteed to be wider -- you might find some odd implementation
that has decided to make all the int types the same width.
There are various ways round this but I won't give any right now since I
get the impression you like to work things out for yourself.
Finally, you *do* have a theoretical overflow problem here:
> int main()
> {
> for(int i = 0; i < 10 * 1000 * 1000; ++i)
> basicPopcount(i);
> }
10 * 1000 * 1000 is larger than the smallest permitted value for INT_MAX
(or, in more C++ terms, std::numeric_limits<int>::max()). You may never
come across an implementation with that narrow an int, but you could
impress an interviewer by showing that you are aware of the issue.
--
Ben.