Bug 29950 - Generated code changes after unrelated edits in source.
Summary: Generated code changes after unrelated edits in source.
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 4.2.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2006-11-22 22:56 UTC by Denis Vlasenko
Modified: 2008-03-29 00:57 UTC (History)
1 user (show)

See Also:
Host: i386-pc-linux-gnu
Target: i386-pc-linux-gnu
Build: i386-pc-linux-gnu
Known to work:
Known to fail:
Last reconfirmed:

Visual comparison of assembly (879 bytes, text/plain)
2006-11-22 22:57 UTC, Denis Vlasenko
Complete testcase with .c, .o and .s files (43.57 KB, application/octet-stream)
2006-11-22 22:58 UTC, Denis Vlasenko
An example of code compiled differently by 4.2.1 (29.79 KB, application/octet-stream)
2007-08-28 14:49 UTC, Denis Vlasenko

Note You need to log in before you can comment on or make changes to this bug.
Description Denis Vlasenko 2006-11-22 22:56:12 UTC
I noticed that sizes of functions in generated code change when I do simple unrelated changes. Example:

# diff -u vi_small.c vi_big.c
--- vi_small.c  2006-11-22 23:35:39.000000000 +0100
+++ vi_big.c    2006-11-22 23:35:42.000000000 +0100
@@ -3414,6 +3414,8 @@
 } sockaddr_inet;
 extern int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen);
 extern int create_and_bind_socket_ip4or6(const char *hostaddr, int port);
+extern int setsockopt_reuseaddr(int fd);
+extern int setsockopt_reuseaddr2(int fd);
 extern char *xstrdup(const char *s);
 extern char *xstrndup(const char *s, int n);
 extern char *safe_strncpy(char *dst, const char *src, size_t size);

Both .c files have this simple function at the end:

Byte *find_pair(Byte * p, Byte c)
 Byte match, *q;
 int dir, level;
 match = ')';
 level = 1;
 dir = 1;
 switch (c) {
 case '(':
  match = ')';
 case '[':
  match = ']';
 case '{':
  match = '}';
 case ')':
  match = '(';
  dir = -1;
 case ']':
  match = '[';
  dir = -1;
 case '}':
  match = '{';
  dir = -1;
 for (q = p + dir; text <= q && q < end; q += dir) {
  if (*q == c)
  if (*q == match)
  if (level == 0)
 if (level != 0)
  q = ((void *)0);
 return (q);

which is obviously does not depend on setsockopt_reuseaddr[2].
I made two extra copies of it, just for fun, but it happens without copies too.
Okay, when I compile those:

function compile() {
    gcc -fno-builtin-strlen -Os "$@"
compile -c -o vi_small.o vi_small.c
compile -c -o vi_big.o   vi_big.c
nm --size-sort vi_small.o | grep find_pair
nm --size-sort vi_big.o   | grep find_pair

I am getting:

000000a9 T find_pair   (vi_small.c)
000000a9 T find_pairB
000000a9 T find_pairC

000000a9 T find_pairB  (vi_big.c)
000000a9 T find_pairC
000000b9 T find_pair   <--------- different??

How come gcc generates different code for identical source files?
You saw the diff, it cannot matter at all...
How come gcc generates different code for identical functions???
(vi_big.c:find_pairB is the same as vi_big.c:find_pair to the last byte)

# gcc -v
Using built-in specs.
Target: i386-pc-linux-gnu
Configured with: ../gcc-4.1.1.org/configure --prefix=/usr/app/gcc-4.1.1.org --exec-prefix=/usr/app/gcc-4.1.1.org --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/app/gcc-4.1.1.org/libexec --datadir=/usr/app/gcc-4.1.1.org/share --sysconfdir=/etc --sharedstatedir=/usr/app/gcc-4.1.1.org/var/com --localstatedir=/usr/app/gcc-4.1.1.org/var --libdir=/usr/lib --includedir=/usr/include --infodir=/usr/info --mandir=/usr/man --with-slibdir=/usr/app/gcc-4.1.1.org/lib --with-local-prefix=/usr/local --with-gxx-include-dir=/usr/app/gcc-4.1.1.org/include/g++-v3 --enable-languages=c,c++ --with-system-zlib --disable-nls --enable-threads=posix i386-pc-linux-gnu
Thread model: posix
gcc version 4.1.1
Comment 1 Denis Vlasenko 2006-11-22 22:57:13 UTC
Created attachment 12669 [details]
Visual comparison of assembly
Comment 2 Denis Vlasenko 2006-11-22 22:58:48 UTC
Created attachment 12670 [details]
Complete testcase with .c, .o and .s files
Comment 3 Andrew Pinski 2006-11-24 02:51:23 UTC
Most likely something is being hashed by addresses and then transversing the hashtable or an unstable sort due to using addresses as the last compare.  A lot of these issues were fixed in 4.2.0 so this might be fixed already.
Comment 4 Denis Vlasenko 2007-07-21 23:41:50 UTC
Tested gcc 4.2.1, generates identical code:

000000aa T find_pair
000000aa T find_pairB
000000aa T find_pairC
000000aa T find_pair
000000aa T find_pairB
000000aa T find_pairC

Also did a diff of .s files:

# diff -u vi_small.s vi_big.s
--- vi_small.s  Sun Jul 22 00:38:33 2007
+++ vi_big.s    Sun Jul 22 00:38:33 2007
@@ -1,4 +1,4 @@
-       .file   "vi_small.c"
+       .file   "vi_big.c"
        .type   begin_line, @function
@@ -505,11 +505,11 @@
        movl    dot, %esi
        movl    %esi, %eax
        call    begin_line
-       cmpl    cur_line.7876, %eax
+       cmpl    cur_line.7880, %eax
        je      .L108
        movl    %esi, %eax
        call    begin_line
-       movl    %eax, cur_line.7876
+       movl    %eax, cur_line.7880
        movl    Ureg, %eax
        movl    %eax, -16(%ebp)
        movl    end, %eax
@@ -694,8 +694,8 @@
        .size   last_file_modified, 4
        .long   -1
-       .local  cur_line.7876
-       .comm   cur_line.7876,4,4
+       .local  cur_line.7880
+       .comm   cur_line.7880,4,4
        .local  vi_setops
        .comm   vi_setops,4,4
        .local  editing

At least this testcase passes now. Maybe this is fixed in 4.2 indeed. Thanks!
Comment 5 Denis Vlasenko 2007-08-28 14:49:17 UTC
Created attachment 14125 [details]
An example of code compiled differently by 4.2.1

It still exists in 4.2.1

testcase-421 contains preporcesses and slightly trimmed sources of less.c:
less3.c originally lacked #include <sys/poll.h>, less4.c had that line.

Nothing in these files actually use poll.h

Function buffer_fill_and_print() is compiled differently:

$ nm --size-sort less3.o less4.o | grep buffer_fill_and_print
00000049 T buffer_fill_and_print
0000004c T buffer_fill_and_print

More details are in README file.
Comment 6 Andrew Pinski 2007-08-31 01:47:09 UTC
Most likely what is happening is that DECL_UIDs are different so they will produce different code because of that.
Comment 7 Denis Vlasenko 2007-08-31 11:30:00 UTC
This is unfortunate, it skews busybox's "make bloatcheck" results.

In general, I suppose if generated asm sequences are different -> one of them is "better" (for some definition of "better") -> gcc generates better/worse code depending on irrelevant factors.In this case gcc saw

typedef unsigned long int nfds_t;
struct pollfd { int fd; short int events; short int revents; };
extern int poll (...);
extern int ppoll (...);

and decided to spill one register more in function which has nothing to do with the above.

btw, Andrew, you seem to sit on gcc bugzilla for a long time. Thank you for your work in such, eh, thankless area!
Comment 8 Andrew Pinski 2008-03-29 00:57:06 UTC
This is working as designed, the unrelated edits are really related as we base stuff on the DECL UID which we assign based on the order.

We only promise the same generated code for the same preprocessed source.  Otherwise you can never do an ordering in a quick way. DECL UID are stable for rerunning of the compiler so obviously adding a function or two will change the UID since the functions are decls too.