Bug 11973 - GCC 3.3 optimizer creates wrong code (i386/Linux)
Summary: GCC 3.3 optimizer creates wrong code (i386/Linux)
Status: RESOLVED DUPLICATE of bug 21920
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.3
: P3 normal
Target Milestone: 3.4.0
Assignee: Not yet assigned to anyone
Keywords: wrong-code
Depends on:
Reported: 2003-08-18 21:14 UTC by Yuri
Modified: 2005-07-23 22:49 UTC (History)
3 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed:


Note You need to log in before you can comment on or make changes to this bug.
Description Yuri 2003-08-18 21:14:47 UTC
Symptom: Code (see below) crashes when compiled with -O3 and works
when compiled non-optimized

Expected: both optimized and nonoptimized should exit "main" OK.

Immediate reason in ASM: in inlined function alc() in E::X_alc()
freed = freed->next and first operation of constructor of C class
(vtbl initialization) are swapped by optimizer.
So it fills VTBL pointer into freed->next before it's value is being
placed into freed by alc(). So after the first call to alc()
freed is already wrong (has VTBL in it). And it should have "next"
value from the previous "freed" instance.

Please contact me yuri@tsoft.com if explanation isn't clear.
This bug prevents us from switching to 3.3.


----Version from "gcc -v"------------------------
Reading specs from /usr/gcc-3.3/bin/../lib/gcc-lib/i686-pc-linux-gnu/3.3/specs
Configured with: ./configure --prefix=/gjt/home/yuri/gcc 
Thread model: posix
gcc version 3.3
---end Version------------------------------------
#include <stdio.h> 
struct SList {
  SList *next;
  int i;

SList sl1, sl2;
SList *freed; 
inline void *alc() {
  void *r = freed;
  freed = freed->next;
  return (r);
class P {
  virtual int fn1() { return (1); }
  virtual int fn2() { return (2); } 

class C : public P {
  virtual int fn1() { return (3); }
  virtual int fn2() { return (4); }

  int i;

  inline void * operator new(size_t sz) { return alc(); }
  inline void operator delete(void *) { }

class D {
  virtual C* X_alc() { return (NULL); }

class E {
  virtual C* X_alc() { return (new C()); };

main(int argc[], const char *argv[]) {
  C *c1, *c2;
  E *a = new E;
  // initialize
  sl1.next = &sl2;
  sl2.next = NULL;
  freed = &sl1;
  printf(" ** freed=%p **\n", freed);
  printf(" ** alloc: %p **\n", (c1 = a->X_alc()));
  printf(" ** freed=%p **\n", freed);

  printf(" ** alloc: %p **\n", (c2 = a->X_alc()));
  printf(" ** freed=%p **\n", freed);

  delete c1;
  delete c2;

  return (0);
---end code---------------------------------------
---asm------(E::X_alc where alc() is inlined)-----
080485e0 <_ZN1E5X_alcEv>:
 80485e0:       55                      push   %ebp
 80485e1:       a1 d8 98 04 08          mov    0x80498d8,%eax
 80485e6:       89 e5                   mov    %esp,%ebp
 80485e8:       c7 00 c8 86 04 08       movl   $0x80486c8,(%eax)
 80485ee:       8b 08                   mov    (%eax),%ecx
 80485f0:       5d                      pop    %ebp
 80485f1:       89 0d d8 98 04 08       mov    %ecx,0x80498d8
 80485f7:       c3                      ret
 80485f8:       90                      nop
 80485f9:       8d b4 26 00 00 00 00    lea    0x0(%esi,1),%esi
---end asm----------------------------------------
Comment 1 Andrew Pinski 2003-08-18 21:47:35 UTC
I think this is another aliasing problem, who knows the rules for C++ aliasing rules should look at 
this.  I think the code is violating them.  In fact this look exactly the same problem as PR 11915, 
even though it uses operator new and this code does not.
Comment 2 Wolfgang Bangerth 2003-08-18 21:55:31 UTC
Your code is invalid. What you do is this: 
- in new a->X_alc() you call C::operator new, which returns a C* which you later access 
- however, C::operator new calls alc(), which returns the memory location of an object 
  which the compiler assumes is of type SList 
Such type games violate C++'s type aliasing rules. The solution is either to fix your 
code, or if you want to play hide-and-seek with the compiler, use -fno-strict-aliasing. 
Comment 3 Andrew Pinski 2003-08-18 22:48:26 UTC
*** Bug 11915 has been marked as a duplicate of this bug. ***
Comment 4 Yuri 2003-08-30 21:37:35 UTC
Since I can not reopen similar to this Bug #11915 which was closed I
reopen this one:

I still think this is bug in GCC even considering aliasing rules.

* in "operator new" object is not created yet so aliasing rules no not apply.

NOTE1: aliasing rules begin to apply the moment after the operator new() exits

NOTE2: also if to treat memory area returned as an object and apply
operator new any "light memory allocating" there will become impossible,
which undermines the meaningfullness of operator new overloading.

Comment 5 Andrew Pinski 2003-08-30 23:07:15 UTC
I still think that this is not true.  There is attribut in GCC (if this is really an aliasing problem) that 
marks the type might alias other types, __may_alias__.
Comment 6 Yuri 2003-08-31 02:04:42 UTC
When "operator new" is called object is non-existent yet.

Aliasing rules talk about accessing two different types with one lvalue.
But in case of "operator new" the second one doesn't exist yet. Therefore
aliasing rules do not apply in this case. We return "void*" from operator new,
not object pointer.

If it was some other function (not operator new) I agree with you totally.

Comment 7 Andrew Pinski 2003-09-01 22:29:23 UTC
Actually since "operator new" is still just a function, your anlysis is not true as they are still 
the same lvalue and inlining the "operator new" just make sure they are the same lvalue 
so the code is still invalid.
Comment 8 Giovanni Bajo 2004-04-02 14:02:34 UTC
Since this bug recently came up againt to my attention, I'd like to note that 
both Segher Boessenkool and Nathan Sidwell confirmed that the code is illegal 
because it breaks aliasing rules.
Comment 9 Andrew Pinski 2005-06-05 08:43:30 UTC
Reopening to ...
Comment 10 Andrew Pinski 2005-06-05 08:43:53 UTC
Mark as a dup of bug 21920.

*** This bug has been marked as a duplicate of 21920 ***