https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83073
--- Comment #2 from Richard Biener <rguenth at gcc dot gnu.org> --- So for VARYING | 1 we go using the may_be_nonzero stuff in else if (code == BIT_IOR_EXPR) { max = wide_int_to_tree (expr_type, may_be_nonzero0 | may_be_nonzero1); wide_int wmin = must_be_nonzero0 | must_be_nonzero1; /* If the input ranges contain only positive values we can truncate the minimum of the result range to the maximum of the input range minima. */ if (int_cst_range0 && int_cst_range1 && tree_int_cst_sgn (vr0.min) >= 0 && tree_int_cst_sgn (vr1.min) >= 0) { wmin = wi::max (wmin, wi::to_wide (vr0.min), TYPE_SIGN (expr_type)); wmin = wi::max (wmin, wi::to_wide (vr1.min), TYPE_SIGN (expr_type)); } /* If either input range contains only negative values we can truncate the minimum of the result range to the respective minimum range. */ if (int_cst_range0 && tree_int_cst_sgn (vr0.max) < 0) wmin = wi::max (wmin, wi::to_wide (vr0.min), TYPE_SIGN (expr_type)); if (int_cst_range1 && tree_int_cst_sgn (vr1.max) < 0) wmin = wi::max (wmin, wi::to_wide (vr1.min), TYPE_SIGN (expr_type)); min = wide_int_to_tree (expr_type, wmin); but for [MIN, MAX] we go /* For op & or | attempt to optimize: [x, y] op z into [x op z, y op z] if z is a constant which (for op | its bitwise not) has n consecutive least significant bits cleared followed by m 1 consecutive bits set immediately above it and either m + n == precision, or (x >> (m + n)) == (y >> (m + n)). The least significant n bits of all the values in the range are cleared or set, the m bits above it are preserved and any bits above these are required to be the same for all values in the range. */ if (vr0p && range_int_cst_p (vr0p)) { wide_int w = wi::to_wide (vr1p->min); int m = 0, n = 0; if (code == BIT_IOR_EXPR) w = ~w; if (wi::eq_p (w, 0)) ... and both cases produce a different outcome (as we see). I don't see how we can do better. Well, we can choose to handle | CST with least significant bit set as ~[0, 0] consistently. Or we can add a predicate effectively_varying_p () and guard the 2nd case above with it. The "proper" result for [MIN,MAX] | 1 is of course a set of every odd number...