https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114086
--- Comment #4 from Jakub Jelinek <jakub at gcc dot gnu.org> --- But sure, confirmed for both: int foo (int e) { switch (e) { case 1: case 3: case 5: case 7: case 9: case 11: case 13: return 1; default: return 0; } } int bar (int e) { switch (e) { case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15: return 1; default: return 0; } } where in foo because we emit the guarding cmpl $13, %edi ja .L1 we could just simplify it to andl $1 when <= 13, and the bar case indeed can be done by (e & -15) != 0; Now, the question is if either of these optimizations should be done in the switch lowering, or if we should do it elsewhere where it would optimize also hand written code like that, if user writes it as int foo2 (int e) { if (e <= 13U) return (10922 >> e) & 1; else return 0; } int bar2 (int e) { if (e <= 15U) return (43690 >> e) & 1; else return 0; } Looking at clang, it can optimize bar, it can't optimize foo (uses switch table rather than shift, that is worse than what gcc emits). And emits pretty much what gcc emits for foo2/bar2. Perhaps phiopt could handle this for the bar2 case and match.pd using range info for foo2? Next question is what should be done if the 2 values aren't 1 and 0, but 0 and 1, or some cst and cst + 1 or cst and cst - 1 for some arbitrary constant cst, or cst and 0, or 0 and cst or cst1 and cst2, whether to emit e.g. a conditional move etc.