https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104334
Bug ID: 104334 Summary: Ranger/dom miscompilation Product: gcc Version: 12.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: jakub at gcc dot gnu.org Target Milestone: --- enum class A { A0, A1, A2, A3 }; int x; __attribute__((noipa)) void baz () { x = 1; } struct B { unsigned b : 2; A foo () const { return static_cast<A> (b); } __attribute__((noinline)) void bar () { if (foo () == A::A2 || foo () == A::A3) baz (); } }; int main () { B c; c.b = 2; c.bar (); if (x != 1) __builtin_abort (); return 0; } is miscompiled on x86_64-linux with -O2 --param logical-op-non-short-circuit=0 and on powerpc64le-linux and s390x-linux with just -O2. Strangely, I can't reproduce it with cross-compilers or -O0 built cc1plus or even cc1plus bootstrapped my usual way: ../configure --enable-languages=default,ada,obj-c++,lto,go,d --enable-checking=yes,rtl,extra --enable-libstdcxx-backtrace=yes but can reproduce it in: ./configure --enable-bootstrap --enable-languages=c,c++ --enable-checking=release --disable-multilib --with-tune=generic --with-build-config=bootstrap-lto --enable-link-serialization=1 (but there only in second and third stages, stage1-gcc still doesn't miscompile it). thread2 dump is still identical between non-miscompiled and miscompiled cases, we have there: <unnamed-unsigned:2> _5; <unnamed-unsigned:2> _7; <bb 2> [local count: 1073741823]: _5 = MEM[(const struct B *)this_3(D)].b; _7 = _5 + 2; if (_7 <= 1) (frankly weird to do the addition and comparison in unsigned:2 type, but it is what reassoc comes up with. Furthermore, for unsigned:2 type, test for value in [2,3] range can be done with just _5 >= 2 and doesn't need to do the addition). The difference is in dom3 dump (-fdump-tree-all-alias-details): @@ -66,11 +66,11 @@ LKUP STMT _5 = MEM[(const struct B *)thi extract_range_from_stmt visiting: _7 = _5 + 2; Intersecting - <unnamed-unsigned:2> VARYING + <unnamed-unsigned:2> [1, +INF] and <unnamed-unsigned:2> VARYING to - <unnamed-unsigned:2> VARYING + <unnamed-unsigned:2> [1, +INF] Optimizing statement _7 = _5 + 2; LKUP STMT _7 = _5 plus_expr 2 2>>> STMT _7 = _5 plus_expr 2 @@ -79,7 +79,7 @@ Optimizing statement if (_7 <= 1) Visiting conditional with predicate: if (_7 <= 1) With known ranges - _7: <unnamed-unsigned:2> VARYING + _7: <unnamed-unsigned:2> [1, +INF] Predicate evaluates to: DON'T KNOW LKUP STMT _7 le_expr 1 @@ -94,9 +94,9 @@ Adding assert for _5 from _5 + 2 <= 1 Intersecting <unnamed-unsigned:2> [0, 1] EQUIVALENCES: { _7 } (1 elements) and - <unnamed-unsigned:2> VARYING + <unnamed-unsigned:2> [1, +INF] to - <unnamed-unsigned:2> [0, 1] EQUIVALENCES: { _7 } (1 elements) + <unnamed-unsigned:2> [1, 1] EQUIVALENCES: { _7 } (1 elements) Intersecting <unnamed-unsigned:2> [2, +INF] EQUIVALENCES: { _5 } (1 elements) and @@ -104,12 +104,12 @@ and to <unnamed-unsigned:2> [2, +INF] EQUIVALENCES: { _5 } (1 elements) Intersecting - <unnamed-unsigned:2> VARYING + <unnamed-unsigned:2> [1, +INF] and - <unnamed-unsigned:2> [0, 1] + <unnamed-unsigned:2> [1, 1] to - <unnamed-unsigned:2> [0, 1] -pushing new range for _7: <unnamed-unsigned:2> [0, 1] EQUIVALENCES: { _7 } (1 elements) + <unnamed-unsigned:2> [1, 1] +pushing new range for _7: <unnamed-unsigned:2> [1, 1] EQUIVALENCES: { _7 } (1 elements) Intersecting <unnamed-unsigned:2> VARYING and @@ -123,7 +123,7 @@ Optimizing statement baz (); <<<< STMT 0 = _7 gt_expr 1 <<<< STMT 1 = _7 le_expr 1 popping range for _5, restoring <unnamed-unsigned:2> VARYING -popping range for _7, restoring <unnamed-unsigned:2> VARYING +popping range for _7, restoring <unnamed-unsigned:2> [1, +INF] Optimizing block #4 @@ -144,6 +144,7 @@ void B::bar (struct B * const this) <bb 2> [local count: 1073741823]: _5 = MEM[(const struct B *)this_3(D)].b; + # RANGE [1, 3] _7 = _5 + 2; if (_7 <= 1) goto <bb 3>; [20.24%] and the bogus # RANGE in the last hunk is what is the problem, both _5 and _7 have actually VARYING range (aka [0, 3]).