This is the mail archive of the gcc-help@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: Crazy compiler optimization


Hello GCC,

I'm facing a wierd compiler optimization problem. Consider the code
snippet below

#include <stdio.h>

int printChar(unsigned long cur_col, unsigned char c)
{
  char buf[256];
  char* bufp = buf;
  char cnt = sizeof(buf) - 2; /* overflow in implicit type conversion */
  unsigned long terminal_width = 500;

  while ((cur_col++ < terminal_width) && cnt) {
      *bufp++ = c;
      cnt--;
  }

  *bufp++ = '\n';
  *bufp = 0;

  printf("%c\n", buf[0]);
  return 1;
}

int main()
{
  printChar(80, '-');
  return 1;
}

While compiler optimization should guarantee that the result of
execution is same at all optimization levels, I'm observing difference
in the result of execution of the above program when optimized to
different levels. Although there is fundamental problem with the
statement "char cnt = sizeof(buf) - 2", GCC seems to be warning(that
too only when -pedantic flag is used) about overflow error while
silently discarding any code related to cnt i.e. the check "&& cnt" in
the if-clause is silently discarded by the compiler at -O2.

$]gcc -g char.c -o char.c.unoptimized -m32 -O0 -Wall -Wextra -pedantic
char.c: In function ‘printChar’:
char.c:8: warning: overflow in implicit constant conversion

$]gcc -g char.c -o char.c.optimized -m32 -O2 -Wall -Wextra -pedantic
char.c: In function ‘printChar’:
char.c:8: warning: overflow in implicit constant conversion

$]./char.c.unoptimized
-
$]./char.c.optimized
-
Segmentation fault (core dumped)

Basically the crash here is because of elimination of the check in the
if-clause "&& cnt" which is causing stack overrun and thereby SIGSEGV.
While standards may say that the behaviour is
undefined when an unsigned value is stored in a signed value, can a
language lawyer explain to me why GCC chose to eliminate code
pertaining to cnt considering it as dead-code ?

Below is the objdump -S output of optimized and unoptimized binaries.

A) Optimized Binary

int printChar(unsigned long cur_col, unsigned char c)
{
 80483b0:    55                       push   %ebp
 80483b1:    89 e5                    mov    %esp,%ebp
 80483b3:    81 ec 08 01 00 00        sub    $0x108,%esp
 80483b9:    8b 45 08                 mov    0x8(%ebp),%eax
  char buf[256];
  char* bufp = buf;
  char cnt = sizeof(buf) - 2;
  unsigned long terminal_width = 500;

  while ((cur_col++ < terminal_width) && cnt) {
 80483bc:    8d 8d 00 ff ff ff        lea    0xffffff00(%ebp),%ecx
 80483c2:    8b 55 0c                 mov    0xc(%ebp),%edx
 80483c5:    3d f3 01 00 00           cmp    $0x1f3,%eax
 80483ca:    77 18                    ja     80483e4 <printChar+0x34>
 80483cc:    83 c0 01                 add    $0x1,%eax
 80483cf:    8d 8d 00 ff ff ff        lea    0xffffff00(%ebp),%ecx
 80483d5:    83 c0 01                 add    $0x1,%eax
      *bufp++ = c;
 80483d8:    88 11                    mov    %dl,(%ecx)
 80483da:    83 c1 01                 add    $0x1,%ecx
 80483dd:    3d f5 01 00 00           cmp    $0x1f5,%eax
 80483e2:    75 f1                    jne    80483d5 <printChar+0x25>
      cnt--;
  }

  *bufp++ = '\n';
 80483e4:    c6 01 0a                 movb   $0xa,(%ecx)
  *bufp = 0;
 80483e7:    c6 41 01 00              movb   $0x0,0x1(%ecx)

  printf("%c\n", buf[0]);
 80483eb:    0f be 85 00 ff ff ff     movsbl 0xffffff00(%ebp),%eax
 80483f2:    c7 04 24 20 85 04 08     movl   $0x8048520,(%esp)
 80483f9:    89 44 24 04              mov    %eax,0x4(%esp)
 80483fd:    e8 b6 fe ff ff           call   80482b8 <printf@plt>
  return 1;
}
 8048402:    b8 01 00 00 00           mov    $0x1,%eax
 8048407:    c9                       leave
 8048408:    c3                       ret
 8048409:    8d b4 26 00 00 00 00     lea    0x0(%esi),%esi


B) Unoptimized binary

int printChar(unsigned long cur_col, unsigned char c)
{
 80483a4:    55                       push   %ebp
 80483a5:    89 e5                    mov    %esp,%ebp
 80483a7:    81 ec 28 01 00 00        sub    $0x128,%esp
 80483ad:    8b 45 0c                 mov    0xc(%ebp),%eax
 80483b0:    88 85 ec fe ff ff        mov    %al,0xfffffeec(%ebp)
  char buf[256];
  char* bufp = buf;
 80483b6:    8d 85 f4 fe ff ff        lea    0xfffffef4(%ebp),%eax
 80483bc:    89 45 f4                 mov    %eax,0xfffffff4(%ebp)
  char cnt = sizeof(buf) - 2;
 80483bf:    c6 45 fb fe              movb   $0xfe,0xfffffffb(%ebp)
  unsigned long terminal_width = 500;
 80483c3:    c7 45 fc f4 01 00 00     movl   $0x1f4,0xfffffffc(%ebp)

  while ((cur_col++ < terminal_width) && cnt) {
 80483ca:    eb 14                    jmp    80483e0 <printChar+0x3c>
      *bufp++ = c;
 80483cc:    0f b6 95 ec fe ff ff     movzbl 0xfffffeec(%ebp),%edx
 80483d3:    8b 45 f4                 mov    0xfffffff4(%ebp),%eax
 80483d6:    88 10                    mov    %dl,(%eax)
 80483d8:    83 45 f4 01              addl   $0x1,0xfffffff4(%ebp)
      cnt--;
 80483dc:    80 6d fb 01              subb   $0x1,0xfffffffb(%ebp)
 80483e0:    8b 45 08                 mov    0x8(%ebp),%eax
 80483e3:    3b 45 fc                 cmp    0xfffffffc(%ebp),%eax
 80483e6:    0f 92 c0                 setb   %al
 80483e9:    83 45 08 01              addl   $0x1,0x8(%ebp)
 80483ed:    83 f0 01                 xor    $0x1,%eax
 80483f0:    84 c0                    test   %al,%al
 80483f2:    75 06                    jne    80483fa <printChar+0x56>
 80483f4:    80 7d fb 00              cmpb   $0x0,0xfffffffb(%ebp)
 80483f8:    75 d2                    jne    80483cc <printChar+0x28>
  }

  *bufp++ = '\n';
 80483fa:    8b 45 f4                 mov    0xfffffff4(%ebp),%eax
 80483fd:    c6 00 0a                 movb   $0xa,(%eax)
 8048400:    83 45 f4 01              addl   $0x1,0xfffffff4(%ebp)
  *bufp = 0;
 8048404:    8b 45 f4                 mov    0xfffffff4(%ebp),%eax
 8048407:    c6 00 00                 movb   $0x0,(%eax)

  printf("%c\n", buf[0]);
 804840a:    0f b6 85 f4 fe ff ff     movzbl 0xfffffef4(%ebp),%eax
 8048411:    0f be c0                 movsbl %al,%eax
 8048414:    89 44 24 04              mov    %eax,0x4(%esp)
 8048418:    c7 04 24 30 85 04 08     movl   $0x8048530,(%esp)
 804841f:    e8 94 fe ff ff           call   80482b8 <printf@plt>
  return 1;
 8048424:    b8 01 00 00 00           mov    $0x1,%eax
}
 8048429:    c9                       leave
 804842a:    c3                       ret

g++ -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--enable-checking=release --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-libgcj-multifile
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk --disable-dssi --disable-plugin
--with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre
--with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-52)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]