1

I tried recent versions of gcc (9.2.1) and clang (9.0.1) for such code:

//pure.cpp
int square (int x) __attribute__ ((pure));

int square (int x)
{
        return  x * x;
}

//test.cpp
#include <stdio.h>

int square (int x) __attribute__ ((pure));

int main(int argc, char *argv[])
{
        const int same = argc;
        printf("result: %d\n", square(same));
        printf("result2: %d\n", square(same));
}

and compile it like this:

g++ -ggdb -Ofast -c test.cpp
g++ -ggdb -Ofast -c pure.cpp
g++ -ggdb -Ofast -o test test.o pure.o

as result I see:

    1043:       e8 58 01 00 00          callq  11a0 <_Z6squarei>
    1048:       48 8d 3d b5 0f 00 00    lea    0xfb5(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
    104f:       89 c6                   mov    %eax,%esi
    1051:       31 c0                   xor    %eax,%eax
    1053:       e8 d8 ff ff ff          callq  1030 <printf@plt>
        printf("result2: %d\n", square(same));
    1058:       89 ef                   mov    %ebp,%edi
    105a:       e8 41 01 00 00          callq  11a0 <_Z6squarei>
    105f:       48 8d 3d aa 0f 00 00    lea    0xfaa(%rip),%rdi        # 2010 <_IO_stdin_used+0x10>
    1066:       89 c6                   mov    %eax,%esi
    1068:       31 c0                   xor    %eax,%eax
    106a:       e8 c1 ff ff ff          callq  1030 <printf@plt>

as you can see there are two calls of _Z6squarei, but why? I marked function as pure, and use the same argument, why gcc and clang can not remove the second call?

user1244932
  • 7,352
  • 5
  • 46
  • 103
  • 1
    Does `__attribute__ ((const))` work? – eerorika Feb 17 '20 at 22:08
  • 1
    I suspect that `__attribute__ ((pure))` is a directive to the _optimizer_. – Drew Dormann Feb 17 '20 at 22:10
  • If you compile with `-O2`, the function is inlined _and_ only calculated _once_. The result is computed/saved in `%ebx` and that is moved into `%esi` [as second arg to `printf`]. For the second `printf` call, `%ebx` is _not_ recalculated but merely moved to `%esi` again. If you force `gcc` to _not_ inline [via `-fno-inline`], then `square` _is_ called but only _once_ [and, again, the computed value is saved in `%ebx`] – Craig Estey Feb 17 '20 at 22:30

1 Answers1

5

The problem is that while square() is marked pure, printf() isn't. So the compiler is not free to assume that all state is the same after a call to printf(), and so square() itself might read different state and thus produce different output. However, it should be fine if you call square() twice without any other function call inbetween:

int main(int argc, char *argv[])
{
        const int same = argc;
        int x = square(same);
        int y = square(same);
        printf("result: %d\n", x);
        printf("result2: %d\n", y);
}

As eerorika mentioned, __attribute__((const)) will work because it adds the further restriction that square() may not read any state except its inputs. See this question about the difference between pure and const.

G. Sliepen
  • 7,637
  • 1
  • 15
  • 31