Bug List: (This bug is not in your last search results)   Show last search results      Search page      Enter new bug
Bug#: 9196
Product:  
Component:  
Status: RESOLVED
Resolution: INVALID
Assigned To: Not yet assigned to anyone <unassigned@gcc.gnu.org>
Host:
Reported against  
Priority:  
Severity:  
Target Milestone:  
 
 
Target:
Reporter: Andreas Jaeger <aj@suse.de>
Add CC:
CC:
Remove selected CCs
Build:
URL:
Summary:
Keywords:
Known to work:
Known to fail:

Attachment Description Type Created Size Actions
Create a New Attachment (proposed patch, testcase, etc.) View All

Bug 9196 depends on: Show dependency tree
Show dependency graph
Bug 9196 blocks:

Additional Comments:






View Bug Activity   |   Format For Printing   |   Clone This Bug


Description:   Last confirmed: 2003-08-23 18:57 Opened: 2003-01-06 10:36
	Using GCC 3.4 I got a failure in the glibc testsuite.  The
	problem can be shown with the appended simplified program:

	gromit:~/tmp:[0]$ /opt/gcc/3.4-devel/bin/gcc -O2 inl.c && ./a.out 
memrchr flunked test 1
1 errors.

Release:
3.4 20030103 (experimental)

Environment:
System: Linux gromit 2.4.18 #1 Sat Apr 6 22:05:01 CEST 2002 i686 unknown
Architecture: i686

	
host: i686-pc-linux-gnu
build: i686-pc-linux-gnu
target: i686-pc-linux-gnu
configured with:

How-To-Repeat:
	Compile this program (this is preproccessed):

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef unsigned int size_t;


extern int printf (__const char *__restrict __format, ...) ;

extern int puts (__const char *__s) ;


extern __inline void *__memrchr (__const void *__s, int __c, size_t __n);

extern __inline void *
__memrchr (__const void *__s, int __c, size_t __n)
{
  register unsigned long int __d0;

  register void *__res;
  if (__n == 0)
    return ((void *)0);

  __asm__ __volatile__
    ("std\n\t"
     "repne; scasb\n\t"
     "je 1f\n\t"
     "orl $-1,%0\n"
     "1:\tcld"
     : "=D" (__res), "=&c" (__d0)
     : "a" (__c), "0" (__s + __n - 1), "1" (__n),
       "m" ( *(struct { __extension__ char __x[__n]; } *)__s)
     : "cc");

  return __res + 1;
}

const char *it = "<UNSET>";
size_t errors = 0;


static void
check (int thing, int number)
{
  if (!thing)
    {
      printf("%s flunked test %d\n", it, number);
      ++errors;
    }
}

char one[50];
char two[50];
char *cp;

static void
test_memrchr (void)
{
  it = "memrchr";
  
  check (__memrchr ("abcd", 'z', 5) == ((void *)0), 1);
}

int
main (void)
{
  int status;


  test_memrchr ();

  if (errors == 0)
    {
      status = 0;
      puts("No errors.");
    }
  else
    {
      status = 1;
      printf("%Zd errors.\n", errors);
    }

  return status;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	Note that GCC 3.3 and earlier releases optimized this
	correctly, this is therefore a regression.

	objdump -dr of the binary shows that the test_memrchr function
	is misoptimized:
080483d0 <test_memrchr>:
 80483d0:       55                      push   %ebp
 80483d1:       ba e0 84 04 08          mov    $0x80484e0,%edx
 80483d6:       89 e5                   mov    %esp,%ebp
 80483d8:       57                      push   %edi
 80483d9:       b8 7a 00 00 00          mov    $0x7a,%eax
 80483de:       83 ec 14                sub    $0x14,%esp
 80483e1:       89 15 14 95 04 08       mov    %edx,0x8049514
 80483e7:       b9 05 00 00 00          mov    $0x5,%ecx
 80483ec:       bf ec 84 04 08          mov    $0x80484ec,%edi
 80483f1:       fd                      std    
 80483f2:       f2 ae                   repnz scas %es:(%edi),%al
 80483f4:       74 03                   je     80483f9 <test_memrchr+0x29>
 80483f6:       83 cf ff                or     $0xffffffff,%edi
 80483f9:       fc                      cld    
 80483fa:       c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp,1)
 8048401:       00 
 8048402:       c7 04 24 00 00 00 00    movl   $0x0,(%esp,1)
 8048409:       e8 82 ff ff ff          call   8048390 <check>

	The result of memrchr is not used - especially not compared
	against NULL and the result passed to check - and instead a
	$0x0 is passed as first argument to check (at address
	8048402).

------- Comment #1 From Andreas Jaeger 2003-01-06 10:36 -------
Fix:
	No fix known for GCC itself.

	The program can be fixed to work by changing the program to
	use (pseudo patch):

-  register void *__res;
+  register int __res;
-    return __res + 1;
+    return (void *) __res + 1;

	But this should not be necessary, the arithmetic on void is a
	GNU extension and therefore legal GNU C code.

------- Comment #2 From Eric Botcazou 2003-02-18 10:08 -------
State-Changed-From-To: open->analyzed
State-Changed-Why: Regression from 3.3 according to Andreas.

------- Comment #3 From Andrew Pinski 2003-06-03 19:14 -------
Moving up priority because it effects glibc.

------- Comment #4 From Andreas Jaeger 2003-06-09 06:02 -------
Bug confirmed with gcc (GCC) 3.4 20030604 (experimental).

------- Comment #5 From Andrew Pinski 2003-07-05 03:07 -------
still happens on the mainline (20030704).

------- Comment #6 From Andrew Pinski 2003-07-28 01:17 -------
Still happens on the mainline (20030727).

------- Comment #7 From Andrew Pinski 2003-08-11 02:55 -------
It is not the char * arithmetic that is being miscompiled as if I replace the
void* inside __memrchr 
with char*, I still get the miscompile.  Also the code is wrong it uses D when
it really meant to use 
d and it should be using "+" instead of "=" aqnd it has enouns additions of
accesing the memory.
__inline void *
__memrchr (__const char *__s, int __c, size_t __n)
{
  register unsigned long int __d0;

  register char *__res;
  if (__n == 0)
    return ((void *)0);
  __res = __s + __n - 1;
  __d0 = __n;

  __asm__ __volatile__
    ("std\n\t"
     "repne; scasb\n\t"
     "je 1f\n\t"
     "orl $-1,%0\n"
     "1:\tcld"
     : "+d" (__res), "+c" (__d0)
     : "a" (__c)
     : "cc");

  return (void*)(__res + 1);
}

If I change "register char *__res;" to "register int __res;", it works so it
looks like it is the pointer 
which causes this.

Also it goes wrong in combine (GCC sets __res dead after asm for some reason).

------- Comment #8 From Andrew Pinski 2003-08-11 18:02 -------
Change summary based on my anlysis last night.

------- Comment #9 From Andrew Pinski 2003-08-23 18:57 -------
The inner has nothing to do with this bug as I can reproduce it when I inlined
it myself:
static void test_memrchr (void)
{
  it = "memrchr";
  void *ret;
  {
  __const char *__s = "abcd";
  int __c = 'z';
  size_t __n = 5;
{
  register unsigned long int __d0;

  register void *__res;
  if (__n == 0)
    { 
      ret = (void*)0;
      goto end;
    }
  __res = __s + __n - 1;
  __d0 = __n;

  __asm__ __volatile__
    ("std\n\t"
     "repne; scasb\n\t"
     "je 1f\n\t"
     "orl $-1,%0\n"
     "1:\tcld"
     : "+d" (__res), "+c" (__d0)
     : "a" (__c)
     : "cc");

  ret = (void*)(__res + 1);
}
  goto end;
  }
end:
  check (ret  == ((void *)0), 1);
}

------- Comment #10 From Zack Weinberg 2003-08-25 20:10 -------
The C semantics for pointer arithmetic mean that GCC is allowed to assume that
this line

  return __res + 1;

never causes a null pointer to be returned.  The "mis-optimized" code has been
optimized based on that assumption.  In short, the bug is in glibc.

The simplest fix is to write "incl %0" as the last instruction of the assembly
block rather than trying to do this calculation in C. 

Bug List: (This bug is not in your last search results)   Show last search results      Search page      Enter new bug