1
votes

I am not being able to explain the following behaviour gcc 4.8.4 exhibits. Let's the following code snippet be named as pow_main.c.

#include <stdio.h>
#include <math.h>

int main()
{
    printf("%lf\n", pow(2, 10));
    return 0;
}

I am compiling the program using the following command-line:

gcc -c -O0 -Wall pow_main.c -o pow_main.o 

Please note that I am only compiling (-c) the program, not linking it. Also, all the compiler optimizations are disabled (-O0). If I look into the symbols contained in the object file, I find:

$ nm pow_main.o
0000000000000000 T main
                 U printf

To me it seems that the compiler has resolved the reference to the method pow statically from the math library or optimized away pow call with some inline code.

  1. How come the symbol pow is not present in the object file?
  2. Isn't the linker responsible for resolve the external references, be it static or dynamic?
  3. Shouldn't there be a undefined (U) symbol pow present in pow_main.o object file?

Here's the platform details:

gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) 

EDIT: Seems one of my two suspicions are correct. gcc has pre-computed the expression and replaced it with a constant.

00000000004006dd <main>:
  4006dd:   55                      push   %rbp
  4006de:   48 89 e5                mov    %rsp,%rbp
  4006e1:   48 83 ec 10             sub    $0x10,%rsp
  4006e5:   48 b8 00 00 00 00 00    movabs $0x4090000000000000,%rax
  4006ec:   00 90 40 
  4006ef:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  4006f3:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  4006f8:   bf e4 07 40 00          mov    $0x4007e4,%edi
  4006fd:   b8 01 00 00 00          mov    $0x1,%eax
  400702:   e8 b9 fe ff ff          callq  4005c0 <printf@plt>
  400707:   48 ba 00 00 00 00 00    movabs $0x4024000000000000,%rdx
  40070e:   00 24 40 
  400711:   48 b8 00 00 00 00 00    movabs $0x4000000000000000,%rax
  400718:   00 00 40 
  40071b:   48 89 55 f8             mov    %rdx,-0x8(%rbp)
  40071f:   f2 0f 10 4d f8          movsd  -0x8(%rbp),%xmm1
  400724:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  400728:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  40072d:   e8 7e fe ff ff          callq  4005b0 <power@plt>
  400732:   f2 0f 11 45 f8          movsd  %xmm0,-0x8(%rbp)
  400737:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  40073b:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  40073f:   f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  400744:   bf e4 07 40 00          mov    $0x4007e4,%edi
  400749:   b8 01 00 00 00          mov    $0x1,%eax
  40074e:   e8 6d fe ff ff          callq  4005c0 <printf@plt>
  400753:   b8 00 00 00 00          mov    $0x0,%eax
  400758:   c9                      leaveq 
  400759:   c3                      retq   
  40075a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Now the question is, how to turn off such an optimization, even at O0?

2
-fno-builtin-pow maybe? - Marc Glisse

2 Answers

1
votes

The compiler can calculate that the result of pow(2, 10) will be 1024 at compile time, so it does that. And, apparently, some computations are done at compile time even at optimization level 0. Hence, there's no need to link the maths library with the program.

Of course, on some systems, such as Mac OS X, you don't need to specify the maths library, -lm, ever; the maths functions are in the main C library.

0
votes
  1. It could be either implemented with a dedicated CPU instruction, or optimised away entirely and replaced with the constant 1024.
  2. Yes, if you have a symbol.
  3. It ain't necessarily so.

If you link a library statically, you normally see resolved symbols marked with T in the output of nm.