https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97555
--- Comment #2 from Andrew Macleod <amacleod at redhat dot com> --- <bb 3> : f.0_1 = f; _2 = 1 % f.0_1; h_24 = (char) _2; _3 = _2; c = _3; _4 = b.a; _5 = (int) _4; _6 = ~_5; f = _6; if (_4 != -1) goto <bb 4>; [INV] else goto <bb 5>; [INV] when calculating the outgoing_range_p() of edge 3->4, we know that then range is != -1. operator_not_equal::op1_range calculates the range on the true side as: if (wi::eq_p (op2.lower_bound(), op2.upper_bound())) { r = op2; r.invert (); } 538 r = op2; (gdb) p op2.dump(stderr) <unnamed-signed:1> [-1, -1]$12 = void (gdb) n 539 r.invert (); (gdb) p r.dump(stderr) <unnamed-signed:1> [-1, -1]$13 = void (gdb) n 543 break; (gdb) p r.dump(stderr) UNDEFINED$14 = void when we invert the range <unnamed-signed:1> [-1, -1] we should get <unnamed-signed:1> [0, 0] but instead its returning UNDEFINED. which when the post dominated merge happens in bb5, we are unioning [-1,-1] and undefined, producing [-1, -1] ofr the range of _4.. if we were unioning [-1, -1] and [0, 0] like we we are suppose to get, we'd get VARYING, and the statement would not be incorrectly folded. You can put a breakpoint in operator_not_equal::op1_range and it should be the first time it is hit.