The following C++ program (preprocessed source attached) produces unexpected results when compiled with -O1 -fstrict-aliasing (as opposed to -O1 only) or with any higher level of optimization (-O2 or -O3). No compilation warnings are emitted in any of the cases. #include <cassert> #include <string> struct A { A() : _x(0.0), _y(0.0) { } float& f(const int& i) { return (i == 0 ? _x : _y); } float _x, _y; }; struct B { B(const char* s) : _s(s) { } std::string g() { return std::string(_s); } const char* _s; }; A h(const char* s) { if (s == 0) return A(); A a; B b(s); if (b.g().compare(std::string("")) == 0) a.f(0) = a.f(1) = 1.0; else if (b.g().compare(std::string(""))) b.g().compare(std::string("")); return a; } int main() { A a(h("")); assert(a._x > 0.0 && a._y > 0.0); return 0; } Compilation results: $ g++ -v Using built-in specs. Target: i686-pc-linux-gnu Configured with: ../src/configure --prefix=/opt/gcc-4.1.1 Thread model: posix gcc version 4.1.1 $ g++ -W -Wall -O1 testcase.cc $ ./a.out $ g++ -W -Wall -O1 -fstrict-aliasing testcase.cc $ ./a.out a.out: testcase.cc:34: int main(): Assertion `a._x > 0.0 && a._y > 0.0' failed. Aborted The assertion ceases to fail (with -O1 -fstrict-aliasing) when adding any (single one) of the options -fno-tree-copy-prop, -fno-tree-dce, -fno-tree-salias or -fno-inline to the compiler command line.
Created attachment 12757 [details] Preprocessed source of test case
Confirmed. There are also no warnings with -Wstrict-aliasing. The code certainly seems ok, so this is either a bug in gcc or libstdc++... W.
This is a regression from 4.0 and the last 4.1 snapshot I have (which is from 20051109 -- I know, I don't get to work on gcc any more :-( ). As per the reporter's report, the bug has apparently been backported to the 4.1 branch. I would rate this as a critical wrong-code regression worth looking at by the aliasing people... W.
Slightly reduced testcase: #include <cassert> #include <string> struct A { A() : _x(0.0), _y(0.0) { } float& f(const int& i) { return (i == 0 ? _x : _y); } float _x, _y; }; A h(const char* s) { A a; std::string b(""); std::string s_(s); if (s_.compare(b) == 0) a.f(0) = a.f(1) = 1.0; else if (std::string(s).compare(std::string(""))) s_.compare(b); return a; } int main() { A a(h("")); assert(a._x > 0.0 && a._y > 0.0); return 0; } For some reason we see the stores to a._x and a._y in TMT.89: # TMT.89_678 = V_MAY_DEF <TMT.89_677>; a._x = 0.0; # TMT.89_679 = V_MAY_DEF <TMT.89_678>; a._y = 0.0; but later discover they have SFTs (after copyprop1 where we cprop &a._x and &a._y into the PHI node from the COND_EXPR inlined from A::f()): # SFT.74_209 = V_MAY_DEF <SFT.74_11>; # SFT.75_208 = V_MAY_DEF <SFT.75_9>; *D.17209_8 = 1.0e+0; # VUSE <SFT.74_209>; # VUSE <SFT.75_208>; D.16796_566 = *D.17209_8; so this is either (again) a pruning issue or we get it wrong in the sense that TMT.89 also is used for integer type.
We have (alias1): Type memory tags TMT.89, UID 17988, struct basic_string<char,std::char_traits<char>,std::allocator<char> >, is an alias tag, is addressable, is global, call clobbered ... TMT.95, UID 17994, float, is addressable, may aliases: { SFT.74 SFT.75 } a, UID 16758, struct A, is addressable, sub-vars: { SFT.75 SFT.74 } Variable: SFT.74, UID 17973, float, is addressable, call clobbered, default def: SFT.74_11, may aliases: { TMT.89 } Variable: SFT.75, UID 17974, float, is addressable, call clobbered, default def: SFT.75_9, may aliases: { TMT.89 } I believe this is all caused because we cannot prove that the string data cannot point to a. Note that the testcase passes if you change the signature from A::f() to get 'i' as value and not reference.
Created attachment 13008 [details] reduced testcase This is a reduced testcase (still requires libstdc++ at link time) where you can see the wrong alias symbols used after copyprop1 with -O -fstrict-aliasing --param max-aliased-vops=0
4.1.0 works, so this is a regression on the branch. Janis, can you hunt what broke it?
Actually the original testcase also fails with 4.1.0.
The test also fails on powerpc-linux, so it's not specific to i686-linux. The results seem to be inconsistent, so I'm doing more testing to determine whether the failure is intermittent.
This is most likely related to PR 30567.
Is this still a problem?
The submitter's testcase fails on powerpc-linux with the current 4.1 and 4.2 branches but has passed on mainline for several months. In comment #9 I said that results seemed to be intermittent; if it would be worthwhile I can try with more revisions between 4.0 and 4.1 to find where it started failing, and also later dates to find when it stopped failing.
(In reply to comment #12) > The submitter's testcase fails on powerpc-linux with the current 4.1 and 4.2 > branches but has passed on mainline for several months. In comment #9 I said > that results seemed to be intermittent; if it would be worthwhile I can try > with more revisions between 4.0 and 4.1 to find where it started failing, and > also later dates to find when it stopped failing. Shouldn't the testcase be added to the testsuite?
The original testcase fails on i686-pc-linux-gnu with -O2 on the 4.1 branch and the 4.2 branch.
*** Bug 33573 has been marked as a duplicate of this bug. ***
The following reduced testcase (which should return 0) returns 1 on the 4.1 branch and 4.2 branch when compiled with "-O -fstrict-aliasing" or "-O2" on i686-pc-linux-gnu: ======================================================= #include <string> struct A { float x, y; A() : x(), y() {} float& foo(const int& i) { return i ? x : y; } }; int main() { if (!"") return 1; A a; if (!std::string("").compare(std::string(""))) a.foo(0) = 1; else if (std::string("").compare(std::string(""))) std::string("").compare(std::string("")); return !a.y; } =======================================================
Trunk is fine.
Closing 4.1 branch.
Closing 4.2 branch, fixed in 4.3.