Consider === example starts === enum class E { a, b, c, d, e, f, g, h, i, j }; bool check_mask(E v) { return v == E::b || v == E::e || v == E::f || v == E::j; } === example ends === This can be compiled to just three instructions (x86): mov $0x232, %eax bt %edi, %eax setc %al but instead gcc compiles it to: cmpl $1, %edi sete %al cmpl $4, %edi sete %dl orb %dl, %al jne .L1 subl $5, %edi andl $-5, %edi sete %al .L1: which is three times as large and contains a possibly unpredictable branch. More bits in the mask will presumably generate larger code.
GCC can do this for switch expansion right now, enum class E { a, b, c, d, e, f, g, h, i, j }; bool check_mask(E v) { switch (v) { case E::b: case E::e: case E::f: case E::j: return true; default: return false; } } xorl %eax, %eax cmpl $9, %edi ja .L1 movl $1, %eax movl %edi, %ecx salq %cl, %rax testl $562, %eax setne %al .L1: ret
Interesting. Still missing optimizations - v cannot be larger than 9 (UB), and bt is faster than mov+shift+and.
UB only with -fstrict-enums which isn't the default.
I expected fold_truth_andor to eventually catch this via fold_range_test. Probably the "bad" association in the testcase prevents this. Anyway, this is a job for reassoc / if-conversion.
Note on the trunk we get the full optimized version with the switch now: check_mask(E): cmpl $9, %edi ja .L3 movl $562, %eax btq %rdi, %rax setc %al ret .L3: xorl %eax, %eax ret
gcc 11.2 produces optimized code with the original example: check_mask(E): cmpl $9, %edi ja .L3 movl %edi, %ecx movl $562, %eax shrq %cl, %rax andl $1, %eax ret .L3: xorl %eax, %eax ret
Marking as fixed, since at least 11.2.