Hi, On Sat, Sep 08, 2007 at 10:16:41PM +0200, Basile STARYNKEVITCH wrote: > Chris Lattner wrote: >> I understand, but allowing users to override new means that the actual >> implementation may not honor the aliasing guarantees of attribute malloc. >> -Chris > > Maybe it could make sense to give the malloc attribute only to ::operator > new but not to other new-s, in particular not to the placement new? > > But I am not a C++ expert!
Neither am I. However, I have done a small series of experiments that showed this is exactly what happens with the patch I sent in the original mail. Obviously, this thread is not about placement new which I believe definitely cannot have the flag set. I compiled four small tests with both patched and untampered compiler (revision 128277) and compared dumps. The exact dumps and some further details are attached, here I'll just list three of the sources and briefly summarize the effect of the patch. 1. Allocating a simple int (in fact the same example as in PR23383): int f(void) { int t; int *a = new int; int *b = new int; *a = 1; *b = 2; t = *a; delete a; delete b; return t; } The patched compiler turnes return into "return 1" and removes the load to t. The unpatched does neither. 2. Allocating a simple class: class A { public: int d; }; int f(void) { int t; A *a = new A; A *b = new A; a->d = 1; b->d = 2; t = a->d; delete a; delete b; return t; } The same thing. Patched version returns 1, original performs load. 3. Allocating a simple class with a redefined new operator. class A { public: int d; static void* operator new (size_t size); static void operator delete (void *p); }; static A pool[2]; static int n = 0; __attribute__ ((noinline)) void* A::operator new (size_t size) { // the following should be dangerous enough: void *p = &pool[n]; n++; return p; } __attribute__ ((noinline)) void A::operator delete (void *p) { // no-op } int f(void) { int t; A *a = new A; A *b = new A; a->d = 1; b->d = 2; t = a->d; delete a; delete b; return t; } Both patched and original compiler produced exactly the same dump, the patched version did not perform any (unsafe) transformations. So my conclusion is that redefined new operators are not an issue and the attribute does not affect them. Martin
All dumps below are from *.121t.optimized files. The only optimization related switch used was -O3. ====================================================================== test.int.cpp ------------ Source: ------- #include <new> int f(void) { int t; int *a = new int; int *b = new int; *a = 1; *b = 2; t = *a; delete a; delete b; return t; } ---------------------------------------------------------------------- Unpatched compiler: ------------------- int f() () { intD.2 * bD.1771; intD.2 * aD.1770; intD.2 tD.1769; voidD.35 * D.1773; voidD.35 * D.1772; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.6D.1783_9 = VDEF <SMT.6D.1783_8(D)> { SMT.6D.1783 } D.1772 = operator new (4); aD.1770 = (intD.2 *) D.1772; # SMT.6D.1783_10 = VDEF <SMT.6D.1783_9> { SMT.6D.1783 } D.1773 = operator new (4); bD.1771 = (intD.2 *) D.1773; # SMT.6D.1783_11 = VDEF <SMT.6D.1783_10> { SMT.6D.1783 } *aD.1770 = 1; # SMT.6D.1783_12 = VDEF <SMT.6D.1783_11> { SMT.6D.1783 } *bD.1771 = 2; # VUSE <SMT.6D.1783_12> { SMT.6D.1783 } tD.1769 = *aD.1770; # SMT.6D.1783_13 = VDEF <SMT.6D.1783_12> { SMT.6D.1783 } operator delete (aD.1770); # SMT.6D.1783_14 = VDEF <SMT.6D.1783_13> { SMT.6D.1783 } operator delete (bD.1771); return tD.1769; # SUCC: EXIT [100.0%] } ---------------------------------------------------------------------- Patched compiler: ----------------- int f() () { intD.2 * bD.1771; intD.2 * aD.1770; voidD.35 * D.1773; voidD.35 * D.1772; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # HEAP.4D.1781_11 = VDEF <HEAP.4D.1781_8(D)> # HEAP.5D.1782_12 = VDEF <HEAP.5D.1782_9(D)> # SMT.8D.1785_13 = VDEF <SMT.8D.1785_10(D)> { HEAP.4D.1781 HEAP.5D.1782 SMT.8D .1785 } D.1772 = operator new (4); aD.1770 = (intD.2 *) D.1772; # HEAP.4D.1781_14 = VDEF <HEAP.4D.1781_11> # HEAP.5D.1782_15 = VDEF <HEAP.5D.1782_12> # SMT.8D.1785_16 = VDEF <SMT.8D.1785_13> { HEAP.4D.1781 HEAP.5D.1782 SMT.8D.17 85 } D.1773 = operator new (4); bD.1771 = (intD.2 *) D.1773; # HEAP.4D.1781_17 = VDEF <HEAP.4D.1781_14> { HEAP.4D.1781 } *aD.1770 = 1; # HEAP.5D.1782_18 = VDEF <HEAP.5D.1782_15> { HEAP.5D.1782 } *bD.1771 = 2; # HEAP.4D.1781_19 = VDEF <HEAP.4D.1781_17> # HEAP.5D.1782_20 = VDEF <HEAP.5D.1782_18> # SMT.8D.1785_21 = VDEF <SMT.8D.1785_16> { HEAP.4D.1781 HEAP.5D.1782 SMT.8D.17 85 } operator delete (aD.1770); # HEAP.4D.1781_22 = VDEF <HEAP.4D.1781_19> # HEAP.5D.1782_23 = VDEF <HEAP.5D.1782_20> # SMT.8D.1785_24 = VDEF <SMT.8D.1785_21> { HEAP.4D.1781 HEAP.5D.1782 SMT.8D.17 85 } operator delete (bD.1771); return 1; # SUCC: EXIT [100.0%] } ====================================================================== test_class_stdnew.cpp --------------------- Source: ------- #include <new> class A { public: int d; }; int f(void) { int t; A *a = new A; A *b = new A; a->d = 1; b->d = 2; t = a->d; delete a; delete b; return t; } ---------------------------------------------------------------------- Unpatched compiler: ------------------- int f() () { struct AD.1767 * bD.1774; struct AD.1767 * aD.1773; intD.2 tD.1772; voidD.35 * D.1776; voidD.35 * D.1775; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.6D.1786_9 = VDEF <SMT.6D.1786_8(D)> { SMT.6D.1786 } D.1775 = operator new (4); aD.1773 = (struct AD.1767 *) D.1775; # SMT.6D.1786_10 = VDEF <SMT.6D.1786_9> { SMT.6D.1786 } D.1776 = operator new (4); bD.1774 = (struct AD.1767 *) D.1776; # SMT.6D.1786_11 = VDEF <SMT.6D.1786_10> { SMT.6D.1786 } aD.1773->dD.1769 = 1; # SMT.6D.1786_12 = VDEF <SMT.6D.1786_11> { SMT.6D.1786 } bD.1774->dD.1769 = 2; # VUSE <SMT.6D.1786_12> { SMT.6D.1786 } tD.1772 = aD.1773->dD.1769; # SMT.6D.1786_13 = VDEF <SMT.6D.1786_12> { SMT.6D.1786 } operator delete (aD.1773); # SMT.6D.1786_14 = VDEF <SMT.6D.1786_13> { SMT.6D.1786 } operator delete (bD.1774); return tD.1772; # SUCC: EXIT [100.0%] } ---------------------------------------------------------------------- Patched compiler: ----------------- int f() () { struct AD.1767 * bD.1774; struct AD.1767 * aD.1773; voidD.35 * D.1776; voidD.35 * D.1775; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # HEAP.4D.1784_11 = VDEF <HEAP.4D.1784_8(D)> # HEAP.5D.1785_12 = VDEF <HEAP.5D.1785_9(D)> # SMT.8D.1788_13 = VDEF <SMT.8D.1788_10(D)> { HEAP.4D.1784 HEAP.5D.1785 SMT.8D .1788 } D.1775 = operator new (4); aD.1773 = (struct AD.1767 *) D.1775; # HEAP.4D.1784_14 = VDEF <HEAP.4D.1784_11> # HEAP.5D.1785_15 = VDEF <HEAP.5D.1785_12> # SMT.8D.1788_16 = VDEF <SMT.8D.1788_13> { HEAP.4D.1784 HEAP.5D.1785 SMT.8D.17 88 } D.1776 = operator new (4); bD.1774 = (struct AD.1767 *) D.1776; # HEAP.4D.1784_17 = VDEF <HEAP.4D.1784_14> { HEAP.4D.1784 } aD.1773->dD.1769 = 1; # HEAP.5D.1785_18 = VDEF <HEAP.5D.1785_15> { HEAP.5D.1785 } bD.1774->dD.1769 = 2; # HEAP.4D.1784_19 = VDEF <HEAP.4D.1784_17> # HEAP.5D.1785_20 = VDEF <HEAP.5D.1785_18> # SMT.8D.1788_21 = VDEF <SMT.8D.1788_16> { HEAP.4D.1784 HEAP.5D.1785 SMT.8D.17 88 } operator delete (aD.1773); # HEAP.4D.1784_22 = VDEF <HEAP.4D.1784_19> # HEAP.5D.1785_23 = VDEF <HEAP.5D.1785_20> # SMT.8D.1788_24 = VDEF <SMT.8D.1788_21> { HEAP.4D.1784 HEAP.5D.1785 SMT.8D.17 88 } operator delete (bD.1774); return 1; # SUCC: EXIT [100.0%] } ====================================================================== test_class_rednew.cpp --------------------- Note: Inlining had to be switchde off, otherwise it and SRA did too good a job. Source: ------- #include <new> class A { public: int d; static void* operator new (size_t size); static void operator delete (void *p); }; static A pool[2]; static int n = 0; __attribute__ ((noinline)) void* A::operator new (size_t size) { // the following should be dangerous enough: void *p = &pool[n]; n++; return p; } __attribute__ ((noinline)) void A::operator delete (void *p) { // no-op } int f(void) { int t; A *a = new A; A *b = new A; a->d = 1; b->d = 2; t = a->d; delete a; delete b; return t; } ---------------------------------------------------------------------- Unpatched compiler: ------------------- int f() () { struct AD.1767 * aD.1789; voidD.35 * D.1792; voidD.35 * D.1791; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.30D.1825_9 = VDEF <SMT.30D.1825_8(D)> { SMT.30D.1825 } D.1791 = operator new (4); aD.1789 = (struct AD.1767 *) D.1791; # SMT.30D.1825_10 = VDEF <SMT.30D.1825_9> { SMT.30D.1825 } D.1792 = operator new (4); # SMT.30D.1825_11 = VDEF <SMT.30D.1825_10> { SMT.30D.1825 } aD.1789->dD.1769 = 1; # SMT.30D.1825_12 = VDEF <SMT.30D.1825_11> { SMT.30D.1825 } ((struct AD.1767 *) D.1792)->dD.1769 = 2; return aD.1789->dD.1769; # SUCC: EXIT [100.0%] } ---------------------------------------------------------------------- Patched compiler: (NO CHANGE) ----------------- int f() () { struct AD.1767 * aD.1789; voidD.35 * D.1792; voidD.35 * D.1791; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.30D.1825_9 = VDEF <SMT.30D.1825_8(D)> { SMT.30D.1825 } D.1791 = operator new (4); aD.1789 = (struct AD.1767 *) D.1791; # SMT.30D.1825_10 = VDEF <SMT.30D.1825_9> { SMT.30D.1825 } D.1792 = operator new (4); # SMT.30D.1825_11 = VDEF <SMT.30D.1825_10> { SMT.30D.1825 } aD.1789->dD.1769 = 1; # SMT.30D.1825_12 = VDEF <SMT.30D.1825_11> { SMT.30D.1825 } ((struct AD.1767 *) D.1792)->dD.1769 = 2; return aD.1789->dD.1769; # SUCC: EXIT [100.0%] } ====================================================================== test_class_rednew2.cpp ---------------------- Source: ------- #include <new> #include <stdlib.h> class A { public: int d; static void* operator new (size_t size); static void operator delete (void *p); }; static A pool[2]; static int n = 0; __attribute__ ((noinline)) void* A::operator new (size_t size) { return malloc (size); } __attribute__ ((noinline)) void A::operator delete (void *p) { free (p); } int f(void) { int t; A *a = new A; A *b = new A; a->d = 1; b->d = 2; t = a->d; delete a; delete b; return t; } ---------------------------------------------------------------------- Unpatched compiler: ------------------- int f() () { struct AD.2539 * bD.2560; struct AD.2539 * aD.2559; intD.2 tD.2558; voidD.35 * D.2562; voidD.35 * D.2561; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.31D.2598_9 = VDEF <SMT.31D.2598_8(D)> { SMT.31D.2598 } D.2561 = operator new (4); aD.2559 = (struct AD.2539 *) D.2561; # SMT.31D.2598_10 = VDEF <SMT.31D.2598_9> { SMT.31D.2598 } D.2562 = operator new (4); bD.2560 = (struct AD.2539 *) D.2562; # SMT.31D.2598_11 = VDEF <SMT.31D.2598_10> { SMT.31D.2598 } aD.2559->dD.2541 = 1; # SMT.31D.2598_12 = VDEF <SMT.31D.2598_11> { SMT.31D.2598 } bD.2560->dD.2541 = 2; # VUSE <SMT.31D.2598_12> { SMT.31D.2598 } tD.2558 = aD.2559->dD.2541; # SMT.31D.2598_13 = VDEF <SMT.31D.2598_12> { SMT.31D.2598 } operator delete (aD.2559); # SMT.31D.2598_14 = VDEF <SMT.31D.2598_13> { SMT.31D.2598 } operator delete (bD.2560); return tD.2558; # SUCC: EXIT [100.0%] } ---------------------------------------------------------------------- Patched compiler: (NO CHANGE) ----------------- int f() () { struct AD.2539 * bD.2560; struct AD.2539 * aD.2559; intD.2 tD.2558; voidD.35 * D.2562; voidD.35 * D.2561; # BLOCK 2 freq:10000 # PRED: ENTRY [100.0%] (fallthru,exec) # SMT.31D.2598_9 = VDEF <SMT.31D.2598_8(D)> { SMT.31D.2598 } D.2561 = operator new (4); aD.2559 = (struct AD.2539 *) D.2561; # SMT.31D.2598_10 = VDEF <SMT.31D.2598_9> { SMT.31D.2598 } D.2562 = operator new (4); bD.2560 = (struct AD.2539 *) D.2562; # SMT.31D.2598_11 = VDEF <SMT.31D.2598_10> { SMT.31D.2598 } aD.2559->dD.2541 = 1; # SMT.31D.2598_12 = VDEF <SMT.31D.2598_11> { SMT.31D.2598 } bD.2560->dD.2541 = 2; # VUSE <SMT.31D.2598_12> { SMT.31D.2598 } tD.2558 = aD.2559->dD.2541; # SMT.31D.2598_13 = VDEF <SMT.31D.2598_12> { SMT.31D.2598 } operator delete (aD.2559); # SMT.31D.2598_14 = VDEF <SMT.31D.2598_13> { SMT.31D.2598 } operator delete (bD.2560); return tD.2558; # SUCC: EXIT [100.0%] }